`
lx82319214
  • 浏览: 109961 次
  • 性别: Icon_minigender_1
  • 来自: 贵州
社区版块
存档分类
最新评论

android Launcher——拖放功能深入研究

 
阅读更多

Luancher有一个相对比较复杂的功能就是拖放功能,要深入了解launcher,深入理解拖放功能是有必要的,这篇blog,我将对launcher的拖放功能做深入的了解
1.首先直观感受什么时候开始拖放?我们长按桌面一个应用图标或者控件的时候拖放就开始了,包括在all app view中长按应用图标,下面就是我截取的拖放开始的代码调用堆栈
at com.android.launcher2.DragController.startDrag(DragController.java:170)
at com.android.launcher2.Workspace.startDrag(Workspace.java:1068)
at com.android.launcher2.Launcher.onLongClick(Launcher.java:1683)
at android.view.View.performLongClick(View.java:2427)
at android.widget.TextView.performLongClick(TextView.java:7286)
at android.view.View$CheckForLongPress.run(View.java:8792)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
桌面应用图标由Launcher.onLongClick负责监听处理,插入断点debug进入onLongclick函数
         if (!(v instanceof CellLayout)) {
            v = (View) v.getParent();
        }
                                 //获取桌面CellLayout上一个被拖动的对象
         CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
               ...
        if (mWorkspace.allowLongPress()) {
            if (cellInfo.cell == null) {
                ...
            } else {
                if (!(cellInfo.cell instanceof Folder)) {
                    ...
                    //调用Workspace.startDrag处理拖动
                    mWorkspace.startDrag(cellInfo);
                }
            }
        }
我上面只写出关键代码,首先是获取被拖动的对象v.getTag(),Tag什么时候被设置进去的了
   public boolean onInterceptTouchEvent(MotionEvent ev) {
        ...
        if (action == MotionEvent.ACTION_DOWN) {
                        ...
            boolean found = false;
            for (int i = count - 1; i >= 0; i--) {
                final View child = getChildAt(i);

                if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
                    child.getHitRect(frame);
                    //判断区域是否在这个子控件的区间,如果有把child信息赋给mCellInfo
                    if (frame.contains(x, y)) {
                        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                        cellInfo.cell = child;
                        cellInfo.cellX = lp.cellX;
                        cellInfo.cellY = lp.cellY;
                        cellInfo.spanX = lp.cellHSpan;
                        cellInfo.spanY = lp.cellVSpan;
                        cellInfo.valid = true;
                        found = true;
                        mDirtyTag = false;
                        break;
                    }
                }
            }
            
            mLastDownOnOccupiedCell = found;

            if (!found) {
                            ...
                            //没有child view 说明没有点击桌面图标项
                cellInfo.cell = null;               
            }
            setTag(cellInfo);
        }
看了上面代码知道,当开始点击桌面时,celllayout就会根据点击区域去查找在该区域是否有child存在,若有把它设置为tag.cell,没有,tag.cell设置为null,后面在开始拖放时launcher.onlongclick中对tag进行处理,
这个理顺了,再深入到workspace.startDrag函数,workspace.startDrag调用DragController.startDrag去处理拖放
mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
再分析一下上面调用的几个参数
child = tag.cell
this = workspace
child.getTag()是什么呢?在什么时候被设置?再仔细回顾原来launcher加载过程代码,在launcher.createShortcut中它被设置了:注意下面我代码中的注释
    View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
        TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);

        favorite.setCompoundDrawablesWithIntrinsicBounds(null,
                new FastBitmapDrawable(info.getIcon(mIconCache)),
                null, null);
        favorite.setText(info.title);
        //设置favorite(一个桌面Shortcut类型的图标)的tag
        favorite.setTag(info);
        favorite.setOnClickListener(this);

        return favorite;
    }
继续深入解读DragController.startDrag函数
    public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
            //设置拖放源view
            mOriginator = v;
        //获取view的bitmap
        Bitmap b = getViewBitmap(v);

        if (b == null) {
            // out of memory?
            return;
        }
        //获取源view在整个屏幕的坐标
        int[] loc = mCoordinatesTemp;
        v.getLocationOnScreen(loc);
        int screenX = loc[0];
        int screenY = loc[1];
                                //该函数功能解读请继续往下看
        startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
                source, dragInfo, dragAction);

        b.recycle();
        //设置原来view不可见
        if (dragAction == DRAG_ACTION_MOVE) {
            v.setVisibility(View.GONE);
        }
    }

////////////////////////////////////////////////////////////
    public void startDrag(Bitmap b, int screenX, int screenY,
            int textureLeft, int textureTop, int textureWidth, int textureHeight,
            DragSource source, Object dragInfo, int dragAction) {
        //隐藏软键盘
        if (mInputMethodManager == null) {
            mInputMethodManager = (InputMethodManager)
                    mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        }
        mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
                                //mListener = deletezone,在blog laucher ui框架中有说明该函数,主要就是现实deletezone
        if (mListener != null) {
            mListener.onDragStart(source, dragInfo, dragAction);
        }
                                //记住手指点击位置与屏幕左上角位置偏差
        int registrationX = ((int)mMotionDownX) - screenX;
        int registrationY = ((int)mMotionDownY) - screenY;

        mTouchOffsetX = mMotionDownX - screenX;
        mTouchOffsetY = mMotionDownY - screenY;

        mDragging = true;
        mDragSource = source;
        mDragInfo = dragInfo;

        mVibrator.vibrate(VIBRATE_DURATION);
                                //创建DragView对象
        DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
                textureLeft, textureTop, textureWidth, textureHeight);
        //显示Dragview对象
        dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
    }
到这里,拖放开始处理的框框基本清楚,但是DragView的创建和显示还有必要进一步深究
        DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
                textureLeft, textureTop, textureWidth, textureHeight);
//函数参数说明:
mContext = launcher
b = 根据拖放源view创建的大小一致的bitmap对象
registrationX = 手指点击位置与拖放源view 坐标x方向的偏移        
registrationY = 手指点击位置与拖放源view 坐标y方向的偏移        
textureLeft = 0
textureTop = 0
textureWidth = b.getWidth()
textureHeight =  b.getHeight()
//函数体
        super(context);
                                //获取window管理器
        mWindowManager = WindowManagerImpl.getDefault();
        //一个动画,开始拖放时显示
        mTween = new SymmetricalLinearTween(false, 110 /*ms duration*/, this);
                                //对源b 做一个缩放产生一个新的bitmap对象
        Matrix scale = new Matrix();
        float scaleFactor = width;
        scaleFactor = mScale = (scaleFactor + DRAG_SCALE) / scaleFactor;
        scale.setScale(scaleFactor, scaleFactor);

        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);

        // The point in our scaled bitmap that the touch events are located
        mRegistrationX = registrationX + (DRAG_SCALE / 2);
        mRegistrationY = registrationY + (DRAG_SCALE / 2);
其实函数很简单,就是记录一些参数,然后对view图片做一个缩放处理,并且准备一个tween动画,在长按桌面图标后图标跳跃到手指上显示该动画,了解这些,有助于理解函数dragView.show
//windowToken来自与workspace.onattchtowindow时候获取的view 所有attch的window标识,有这个参数,可以把dragview添加到
workspace所属的同一个window对象
//touchX,手指点击在屏幕的位置x
//touchy,手指点击在屏幕的位置y
    public void show(IBinder windowToken, int touchX, int touchY) {
        WindowManager.LayoutParams lp;
        int pixelFormat;

        pixelFormat = PixelFormat.TRANSLUCENT;
        //布局参数值的注意的是view位置参数,
        //x=touchX-mRegistrationX=touchX-(registrationX + (DRAG_SCALE / 2))=手指点击位置-view坐标与手指点击位置偏差加上缩放值
        lp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                touchX-mRegistrationX, touchY-mRegistrationY,
                WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                    /*| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM*/,
                pixelFormat);
//        lp.token = mStatusBarView.getWindowToken();
        lp.gravity = Gravity.LEFT | Gravity.TOP;
        lp.token = windowToken;
        lp.setTitle("DragView");
        mLayoutParams = lp;
                                //dragview的父类是Window,也就是说dragview可以拖放到屏幕的任意位置
        mWindowManager.addView(this, lp);

        mAnimationScale = 1.0f/mScale;
        //播放开始拖动动画(直观感觉是图标变大了)
        mTween.start(true);
    }

2,拖放过程
拖放过程的处理需要深入了解DragController.onTouchEvent(MotionEvent ev)函数的实现,我下面列出关键的MotionEvent.ACTION_MOVE部分代码并作出注释说明
                        case MotionEvent.ACTION_MOVE:
                                // 根据手指坐标移动dragview
                                mDragView.move((int) ev.getRawX(), (int) ev.getRawY());

                                // 根据手指所在屏幕坐标获取目前所在的拖放目的view
                                final int[] coordinates = mCoordinatesTemp;
                                DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
                                // 根据不同状态调用DropTarget的生命周期处理函数
                                if (dropTarget != null) {
                                        if (mLastDropTarget == dropTarget) {
                                                dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
                                                                (int) mTouchOffsetY, mDragView, mDragInfo);
                                        } else {
                                                if (mLastDropTarget != null) {
                                                        mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
                                                                        (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
                                                }
                                                dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
                                                                (int) mTouchOffsetY, mDragView, mDragInfo);
                                        }
                                } else {
                                        if (mLastDropTarget != null) {
                                                mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
                                                                (int) mTouchOffsetY, mDragView, mDragInfo);
                                        }
                                }
                                mLastDropTarget = dropTarget;

                                //判断是否在delete区域
                                boolean inDeleteRegion = false;
                                if (mDeleteRegion != null) {
                                        inDeleteRegion = mDeleteRegion.contains(screenX, screenY);
                                }
                                 //不在delete区域,在左边切换区
                                if (!inDeleteRegion && screenX < SCROLL_ZONE) {
                                        if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                                                mScrollState = SCROLL_WAITING_IN_ZONE;
                                                mScrollRunnable.setDirection(SCROLL_LEFT);
                                                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
                                        }
                                }
                                //不在delete区,在右边切换区
                                else if (!inDeleteRegion && screenX > scrollView.getWidth() - SCROLL_ZONE) {
                                        if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                                                mScrollState = SCROLL_WAITING_IN_ZONE;
                                                mScrollRunnable.setDirection(SCROLL_RIGHT);
                                                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
                                        }
                                }
                                //在delete区域
                                else {
                                        if (mScrollState == SCROLL_WAITING_IN_ZONE) {
                                                mScrollState = SCROLL_OUTSIDE_ZONE;
                                                mScrollRunnable.setDirection(SCROLL_RIGHT);
                                                mHandler.removeCallbacks(mScrollRunnable);
                                        }
                                }

                                break;
拖放过程总的处理思路就是根据当前坐标位置获取dropTarget的目标位置,然后又根据相关状态和坐标位置调用dropTarget的对应生命周期函数,这里面有两个点需要进一步深入了解,一是查找dropTarget:findDropTarget(screenX, screenY, coordinates),二是mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
--1.findDropTarget
    private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
        final Rect r = mRectTemp;
                                //mDropTargets是一个拖放目标view别表,在laucher初始化等被添加
        final ArrayList<DropTarget> dropTargets = mDropTargets;
        final int count = dropTargets.size();
        //遍历dropTargets列表,查看{x,y}是否落在dropTarget坐标区域,若是,返回dropTarget。
        for (int i=count-1; i>=0; i--) {
            final DropTarget target = dropTargets.get(i);
            target.getHitRect(r);
            //获取target左上角屏幕坐标
            target.getLocationOnScreen(dropCoordinates);
            r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
            if (r.contains(x, y)) {
                dropCoordinates[0] = x - dropCoordinates[0];
                dropCoordinates[1] = y - dropCoordinates[1];
                return target;
            }
        }
        return null;
    }
--2.mScrollRunnable
//看mScrollRunnable对象的构造类,通过setDirection设置滚动方向,然后通过一步调用DragScroller.scrollLeft/scrollRight来对桌面进行向左向右滚动,想深入了解如何实现的,敬请阅读我相关blogauncher——桌面移动详解
    private class ScrollRunnable implements Runnable {
        private int mDirection;

        ScrollRunnable() {
        }

        public void run() {
            if (mDragScroller != null) {
                if (mDirection == SCROLL_LEFT) {
                    mDragScroller.scrollLeft();
                } else {
                    mDragScroller.scrollRight();
                }
                mScrollState = SCROLL_OUTSIDE_ZONE;
            }
        }

        void setDirection(int direction) {
            mDirection = direction;
        }
    }
3.拖放结束,入口还是在DragController.onTouchEvent(MotionEvent ev)
        先看调用堆栈:
at com.android.launcher2.DragController.endDrag(DragController.java:315)
at com.android.launcher2.DragController.onTouchEvent(DragController.java:471)
at com.android.launcher2.DragLayer.onTouchEvent(DragLayer.java:64)
at android.view.View.dispatchTouchEvent(View.java:3766)
        onTouchEvent关键代码:
                        case MotionEvent.ACTION_UP:
                                mHandler.removeCallbacks(mScrollRunnable);
                                if (mDragging) {
                                        // 拖动过程手指离开屏幕
                                        drop(screenX, screenY);
                                }
                                endDrag();
                                break;
--1.drop(screenX, screenY);
        final int[] coordinates = mCoordinatesTemp;
        //获取dropTarget对象
        DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
        //coordinates=点触点在dropTarget 中的xy坐标

        if (dropTarget != null) {
            dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
                    (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
                    //根据相关参数判断是否可dropTarget是否接受该drag view
            if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1],
                    (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) {
                dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1],
                        (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
                mDragSource.onDropCompleted((View) dropTarget, true);
                return true;
            } else {
                mDragSource.onDropCompleted((View) dropTarget, false);
                return true;
            }
        }
        return false

分享到:
评论

相关推荐

    安卓Android源码——Launcher2.rar

    总之,研究`安卓Android源码——Launcher2`能够帮助开发者深入理解Android系统的桌面逻辑和交互机制,提升APP开发和系统优化的能力。通过分析源码,我们可以学习到Android的UI设计、数据存储、事件处理、动画实现等...

    安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.zip

    本压缩包"安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.zip"提供了对小米Launcher(ZAKER风格)跨屏拖动item功能的实现,这对于开发者来说是一个宝贵的资源,可以帮助他们学习如何构建类似的应用启动器...

    android4.1 桌面模块源码 Launcher2.rar

    总之,Android 4.1 JellyBean 的 Launcher2 源码提供了一个宝贵的教育资源,让开发者有机会深入研究 Android 桌面的实现细节,从而更好地理解和改进这个核心组件。通过细致的阅读和实践,开发者可以掌握更多的 ...

    Delphi 12.3控件之TraeSetup-stable-1.0.12120.exe

    Delphi 12.3控件之TraeSetup-stable-1.0.12120.exe

    基于GPRS,GPS的电动汽车远程监控系统的设计与实现.pdf

    基于GPRS,GPS的电动汽车远程监控系统的设计与实现.pdf

    基于MATLAB/Simulink 2018a的单机无穷大系统暂态稳定性仿真与故障分析

    内容概要:本文详细介绍了如何利用MATLAB/Simulink 2018a进行单机无穷大系统的暂态稳定性仿真。主要内容包括搭建同步发电机模型、设置无穷大系统等效电源、配置故障模块及其控制信号、优化求解器设置以及绘制和分析转速波形和摇摆曲线。文中还提供了多个实用脚本,如故障类型切换、摇摆曲线计算和极限切除角的求解方法。此外,作者分享了一些实践经验,如避免常见错误和提高仿真效率的小技巧。 适合人群:从事电力系统研究和仿真的工程师和技术人员,尤其是对MATLAB/Simulink有一定基础的用户。 使用场景及目标:适用于需要进行电力系统暂态稳定性分析的研究项目或工程应用。主要目标是帮助用户掌握单机无穷大系统的建模和仿真方法,理解故障对系统稳定性的影响,并能够通过仿真结果评估系统的性能。 其他说明:文中提到的一些具体操作和脚本代码对于初学者来说可能会有一定的难度,建议结合官方文档或其他教程一起学习。同时,部分技巧和经验来自于作者的实际操作,具有一定的实用性。

    【KUKA 机器人资料】:KUKA机器人剑指未来——访库卡自动化设备(上海)有限公司销售部经理邹涛.pdf

    KUKA机器人相关资料

    基于DLR模型的PM10–能见度–湿度相关性 研究.pdf

    基于DLR模型的PM10–能见度–湿度相关性 研究.pdf

    MATLAB/Simulink中基于电导增量法的光伏并网系统MPPT仿真及其环境适应性分析

    内容概要:本文详细介绍了如何使用MATLAB/Simulink进行光伏并网系统的最大功率点跟踪(MPPT)仿真,重点讨论了电导增量法的应用。首先阐述了电导增量法的基本原理,接着展示了如何在Simulink中构建光伏电池模型和MPPT控制系统,包括Boost升压电路的设计和PI控制参数的设定。随后,通过仿真分析了不同光照强度和温度条件对光伏系统性能的影响,验证了电导增量法的有效性,并提出了针对特定工况的优化措施。 适合人群:从事光伏系统研究和技术开发的专业人士,尤其是那些希望通过仿真工具深入理解MPPT控制机制的人群。 使用场景及目标:适用于需要评估和优化光伏并网系统性能的研发项目,旨在提高系统在各种环境条件下的最大功率点跟踪效率。 其他说明:文中提供了详细的代码片段和仿真结果图表,帮助读者更好地理解和复现实验过程。此外,还提到了一些常见的仿真陷阱及解决方案,如变步长求解器的问题和PI参数整定技巧。

    【KUKA 机器人坐标的建立】:mo2_base_en.ppt

    KUKA机器人相关文档

    风力发电领域双馈风力发电机(DFIG)Simulink模型的构建与电流电压波形分析

    内容概要:本文详细探讨了双馈风力发电机(DFIG)在Simulink环境下的建模方法及其在不同风速条件下的电流与电压波形特征。首先介绍了DFIG的基本原理,即定子直接接入电网,转子通过双向变流器连接电网的特点。接着阐述了Simulink模型的具体搭建步骤,包括风力机模型、传动系统模型、DFIG本体模型和变流器模型的建立。文中强调了变流器控制算法的重要性,特别是在应对风速变化时,通过实时调整转子侧的电压和电流,确保电流和电压波形的良好特性。此外,文章还讨论了模型中的关键技术和挑战,如转子电流环控制策略、低电压穿越性能、直流母线电压脉动等问题,并提供了具体的解决方案和技术细节。最终,通过对故障工况的仿真测试,验证了所建模型的有效性和优越性。 适用人群:从事风力发电研究的技术人员、高校相关专业师生、对电力电子控制系统感兴趣的工程技术人员。 使用场景及目标:适用于希望深入了解DFIG工作原理、掌握Simulink建模技能的研究人员;旨在帮助读者理解DFIG在不同风速条件下的动态响应机制,为优化风力发电系统的控制策略提供理论依据和技术支持。 其他说明:文章不仅提供了详细的理论解释,还附有大量Matlab/Simulink代码片段,便于读者进行实践操作。同时,针对一些常见问题给出了实用的调试技巧,有助于提高仿真的准确性和可靠性。

    linux之用户管理教程.md

    linux之用户管理教程.md

    三菱PLC与组态王构建3x3书架式堆垛立体库:IO分配、梯形图编程及组态画面设计

    内容概要:本文详细介绍了利用三菱PLC(特别是FX系列)和组态王软件构建3x3书架式堆垛式立体库的方法。首先阐述了IO分配的原则,明确了输入输出信号的功能,如仓位检测、堆垛机运动控制等。接着深入解析了梯形图编程的具体实现,包括基本的左右移动控制、复杂的自动寻址逻辑,以及确保安全性的限位保护措施。还展示了接线图和原理图的作用,强调了正确的电气连接方式。最后讲解了组态王的画面设计技巧,通过图形化界面实现对立体库的操作和监控。 适用人群:从事自动化仓储系统设计、安装、调试的技术人员,尤其是熟悉三菱PLC和组态王的工程师。 使用场景及目标:适用于需要提高仓库空间利用率的小型仓储环境,旨在帮助技术人员掌握从硬件选型、电路设计到软件编程的全流程技能,最终实现高效稳定的自动化仓储管理。 其他说明:文中提供了多个实用的编程技巧和注意事项,如避免常见错误、优化性能参数等,有助于减少实际应用中的故障率并提升系统的可靠性。

    基于STM32的循迹避障小车仿真20250426(带讲解视频)

    基于STM32的循迹避障小车 主控:STM32 显示:OLED 电源模块 舵机云台 超声波测距 红外循迹模块(3个,左中右) 蓝牙模块 按键(6个,模式和手动控制小车状态) TB6612驱动的双电机 功能: 该小车共有3种模式: 自动模式:根据红外循迹和超声波测距模块决定小车的状态 手动模式:根据按键的状态来决定小车的状态 蓝牙模式:根据蓝牙指令来决定小车的状态 自动模式: 自动模式下,检测距离低于5cm小车后退 未检测到任何黑线,小车停止 检测到左边或左边+中间黑线,小车左转 检测到右边或右边+中间黑线,小车右转 检测到中边或左边+中间+右边黑线,小车前进 手动模式:根据按键的状态来决定小车的状态 蓝牙模式: //需切换为蓝牙模式才能指令控制 *StatusX X取值为0-4 0:小车停止 1:小车前进 2:小车后退 3:小车左转 4:小车右转

    海西蒙古族藏族自治州乡镇边界,矢量边界,shp格式

    矢量边界,行政区域边界,精确到乡镇街道,可直接导入arcgis使用

    基于IEEE33节点的主动配电网优化:含风光储柴燃多源调度模型的经济运行研究

    内容概要:本文探讨了基于IEEE33节点的主动配电网优化方法,旨在通过合理的调度模型降低配电网的总运行成本。文中详细介绍了模型的构建,包括风光发电、储能装置、柴油发电机和燃气轮机等多种分布式电源的集成。为了实现这一目标,作者提出了具体的约束条件,如储能充放电功率限制和潮流约束,并采用了粒子群算法进行求解。通过一系列实验验证,最终得到了优化的分布式电源运行计划,显著降低了总成本并提高了系统的稳定性。 适合人群:从事电力系统优化、智能电网研究的专业人士和技术爱好者。 使用场景及目标:适用于需要优化配电网运行成本的研究机构和企业。主要目标是在满足各种约束条件下,通过合理的调度策略使配电网更加经济高效地运行。 其他说明:文章不仅提供了详细的理论推导和算法实现,还分享了许多实用的经验技巧,如储能充放电策略、粒子群算法参数选择等。此外,通过具体案例展示了不同电源之间的协同作用及其经济效益。

    【KUKA 机器人资料】:KUKA 机器人初级培训教材.pdf

    KUKA机器人相关文档

    基于MATLAB的CSP电站与ORC综合能源系统优化建模及应用

    内容概要:本文详细介绍了将光热电站(CSP)和有机朗肯循环(ORC)集成到综合能源系统中的优化建模方法。主要内容涵盖系统的目标函数设计、关键设备的约束条件(如CSP储热罐、ORC热电耦合)、以及具体实现的技术细节。文中通过MATLAB和YALMIP工具进行建模,采用CPLEX求解器解决混合整数规划问题,确保系统在经济性和环境效益方面的最优表现。此外,文章还讨论了碳排放惩罚机制、风光弃能处理等实际应用场景中的挑战及其解决方案。 适合人群:从事综合能源系统研究的专业人士,尤其是对光热发电、余热利用感兴趣的科研工作者和技术开发者。 使用场景及目标:适用于需要评估和优化包含多种能源形式(如光伏、风电、燃气锅炉等)在内的复杂能源系统的项目。目标是在满足供电供热需求的同时,最小化运行成本并减少碳排放。 其他说明:文中提供了大量具体的MATLAB代码片段作为实例,帮助读者更好地理解和复现所提出的优化模型。对于初学者而言,建议从简单的确定性模型入手,逐渐过渡到更复杂的随机规划和鲁棒优化。

    网站设计与管理作业一.ppt

    网站设计与管理作业一.ppt

    基于MATLAB的双闭环Buck电路仿真模型设计与优化

    内容概要:本文详细介绍了如何使用MATLAB搭建双闭环Buck电路的仿真模型。首先定义了主电路的关键参数,如输入电压、电感、电容等,并解释了这些参数的选择依据。接着分别对电压外环和电流内环进行了PI控制器的设计,强调了电流环响应速度需要显著高于电压环以确保系统的稳定性。文中还讨论了仿真过程中的一些关键技术细节,如PWM死区时间的设置、低通滤波器的应用以及参数调整的方法。通过对比单闭环和双闭环系统的性能,展示了双闭环方案在应对负载突变时的优势。最后分享了一些调试经验和常见问题的解决方案。 适合人群:从事电力电子、电源设计领域的工程师和技术人员,尤其是有一定MATLAB基础的读者。 使用场景及目标:适用于需要进行电源管理芯片设计验证、电源系统性能评估的研究人员和工程师。主要目标是提高电源系统的稳定性和响应速度,特别是在负载变化剧烈的情况下。 其他说明:文章不仅提供了详细的理论分析,还包括了大量的代码片段和具体的调试步骤,帮助读者更好地理解和应用所学知识。同时提醒读者注意仿真与实际情况之间的差异,鼓励在实践中不断探索和改进。

Global site tag (gtag.js) - Google Analytics