Android拖动,缩放,自定义内容,控件制作(可拖动缩放RelativeLayout定制)

2,662 阅读14分钟
原文链接: blog.csdn.net

先上效果图:



一.     制作此控件的起源

项目需要一个可以拖动的控件,在网上可以找到很多例子,有图片拖动控件,有textview拖动控件。但是项目中需要控件同时可以动态通过手指调整尺寸,并且控件的内容不固定,需要自定义内容,即可以添加任意内容到拖动控件内。因此,编写此控件。

二.     根据需求做技术分析

1.     可拖动+调整尺寸:view的(scrollTo、scrollBy),设置LayoutParams,覆盖layout方法

2.     自定义内容:需要自定义的控件内存放其他控件,则需要自定义控件继承ViewGroup(LinearLayout、ReletiveLayout)

三.     Android自定义控件所需基础知识

a 位置坐标:

屏幕左上角是坐标原点(0,0),原点向右延伸是x正轴方向,原点向下延伸是y轴正方向

自定义控件的坐标位置是相对于父控件的:getTop()、getBottom(),getLeft(),getRight(),这几个函数用于获取自定义view在父布局坐标系的位置。

b  触摸感知

继承onTouchEvent,获取用户对自定义控件的触摸事件(down,move,up)

根据触摸的位置event.getX(),event.getY(),以及其他位置,判断要执行的的操作。包括根据位移移动,根据位移缩放。根据位移判断是否到达边界。

c 自定义控件父类选择

由于需求中控件里面的内容不定,即可以动态添加任意类型的Android控件到自定义的控件里面,因此这个自定义控件不能通过继承View实现,需要继承ViewGroup来实现,为了使用一些布局功能,最后项目选定继承ViewGroup的子类RelativeLayout,以实现动态添加任意多个任意类型的View控件。

c  移动控件

上文提到移动控件的三个方法:view的(scrollTo、scrollBy), 设置LayoutParams,覆盖layout方法。

Layout:我测试过layout移动控件,是可以达到移动控件的目的,但是破坏了控件尺寸计算路径,(本来是onmeasure之后,onlayout,由于我是自定义viewGroup现在是直接改动了layout,导致控件尺寸错乱,最后造成ViewGroup里面的内容显示不完整),layout介入了view/ViewGroup的地层绘制过程,造成混乱。

LayoutParams:这个参数一般是用于Android的xml布局文件里面,比如:layoutout_height=”” , layout_width=”” ,layout_marginLeft=””,layout_marginTop=””

假定在一个LinearLayout里面放一个imageView,通过修改ImageView的这几个参数就可以让ImageView在LinearLayout里面自由的移动位置:如图:

对自定义控件的位置设置转化为:(marginLeft,marginTop,width,height)

其中,marginLeft和marginTop负责确定控件的位置,width和height确定控件的大小,(可以看着图按自己的方式理解),总之是,通过这几参数的修改,可以使得控件在LinearLayout或RelativeLayout布局内自由的移动并且变换大小。

四.     可移动控件代码编写

原理都写清楚了,开始编写代码,定义一个类继承RelativeLayout,覆盖,onTouchEvent,然后编写逻辑代码:核心代码如下(都粘贴上看着心累):

[java] view plain copy print?
  1. public class MoveLayout extends RelativeLayout {  
  2.   
  3. @Override  
  4.     public boolean onTouchEvent(MotionEvent event) {  
  5.         int action = event.getAction();  
  6.         switch (action) {  
  7.             case MotionEvent.ACTION_DOWN:  
  8.                 oriLeft = getLeft();  
  9.                 oriRight = getRight();  
  10.                 oriTop = getTop();  
  11.                 oriBottom = getBottom();  
  12.   
  13.                 lastY = (int) event.getRawY();  
  14.                 lastX = (int) event.getRawX();  
  15.                 dragDirection = getDirection((int) event.getX(), ( int) event.getY());  
  16.                 break;  
  17.             case MotionEvent.ACTION_UP:  
  18.                              break;  
  19.             case MotionEvent.ACTION_MOVE:  
  20.                 int tempRawX = (int)event.getRawX();  
  21.                 int tempRawY = (int)event.getRawY();  
  22.   
  23.                 int dx = tempRawX - lastX;  
  24.                 int dy = tempRawY - lastY;  
  25.                 lastX = tempRawX;  
  26.                 lastY = tempRawY;  
  27.   
  28.                 switch (dragDirection) {  
  29.                     case LEFT:  
  30.                         left( dx);  
  31.                         break;  
  32.                     case RIGHT:  
  33.                         right( dx);  
  34.                         break;  
  35.                     case BOTTOM:  
  36.                         bottom(dy);  
  37.                         break;  
  38.                     case TOP:  
  39.                         top( dy);  
  40.                         break;  
  41.                     case CENTER:  
  42.                         center( dx, dy);  
  43.                         break;  
  44.                 }  
  45.   
  46.                 //把新的位置 oriLeft, oriTop, oriRight, oriBottom设置到控件,实现位置移动和大小变化。  
  47.                 RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(oriRight - oriLeft, oriBottom - oriTop);  
  48.                 lp.setMargins(oriLeft,oriTop,0,0);  
  49.                 setLayoutParams(lp);  
  50.                            break;  
  51.         }  
  52.         return super.onTouchEvent(event);  
  53.     }  
  54. /** 
  55.  * 触摸点为中心->>移动 
  56.  */  
  57. private void center(int dx, int dy) {  
  58.     int left = getLeft() + dx;  
  59.     int top = getTop() + dy;  
  60.     int right = getRight() + dx;  
  61.     int bottom = getBottom() + dy;  
  62.   
  63.     if (left < 0) {  
  64.         left = 0;  
  65.         right = left + getWidth();  
  66.     }  
  67.     if (right > screenWidth ) {  
  68.         right = screenWidth ;  
  69.         left = right - getWidth();  
  70.     }  
  71.     if (top < 0) {  
  72.         top = 0;  
  73.         bottom = top + getHeight();  
  74.     }  
  75.     if (bottom > screenHeight ) {  
  76.         bottom = screenHeight ;  
  77.         top = bottom - getHeight();  
  78.     }  
  79.   
  80.     oriLeft = left;  
  81.     oriTop = top;  
  82.     oriRight = right;  
  83.     oriBottom = bottom;  
  84. }  
  85. }  
public class MoveLayout extends RelativeLayout {

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                oriLeft = getLeft();
                oriRight = getRight();
                oriTop = getTop();
                oriBottom = getBottom();

                lastY = (int) event.getRawY();
                lastX = (int) event.getRawX();
                dragDirection = getDirection((int) event.getX(), (int) event.getY());
                break;
            case MotionEvent.ACTION_UP:
                             break;
            case MotionEvent.ACTION_MOVE:
                int tempRawX = (int)event.getRawX();
                int tempRawY = (int)event.getRawY();

                int dx = tempRawX - lastX;
                int dy = tempRawY - lastY;
                lastX = tempRawX;
                lastY = tempRawY;

                switch (dragDirection) {
                    case LEFT:
                        left( dx);
                        break;
                    case RIGHT:
                        right( dx);
                        break;
                    case BOTTOM:
                        bottom(dy);
                        break;
                    case TOP:
                        top( dy);
                        break;
                    case CENTER:
                        center( dx, dy);
                        break;
                }

                //把新的位置 oriLeft, oriTop, oriRight, oriBottom设置到控件,实现位置移动和大小变化。
                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(oriRight - oriLeft, oriBottom - oriTop);
                lp.setMargins(oriLeft,oriTop,0,0);
                setLayoutParams(lp);
                           break;
        }
        return super.onTouchEvent(event);
    }
/**
 * 触摸点为中心->>移动
 */
private void center(int dx, int dy) {
    int left = getLeft() + dx;
    int top = getTop() + dy;
    int right = getRight() + dx;
    int bottom = getBottom() + dy;

    if (left < 0) {
        left = 0;
        right = left + getWidth();
    }
    if (right > screenWidth ) {
        right = screenWidth ;
        left = right - getWidth();
    }
    if (top < 0) {
        top = 0;
        bottom = top + getHeight();
    }
    if (bottom > screenHeight ) {
        bottom = screenHeight ;
        top = bottom - getHeight();
    }

    oriLeft = left;
    oriTop = top;
    oriRight = right;
    oriBottom = bottom;
}
}

五.     控件管理代码

以上实现了一个拖动和改变大小的控件,其实就是实现了一个定制的RelativeLayout,定制的RelativeLayout可以被拖动,和改变大小。因此可以在MoveLayout内添加任意view实现自己的显示效果。

 

现在为了能在一个布局上动态的增加很多个可移动的控件,并且对这些控件做管理功能,(动态增加、动态删除)

为了实现这个功能,又自定义了一个RelativeLayout来放置多个MoveLayout,动态增加,删除。新自定义的RelativeLayout叫做:DragView:简略代码如下:

[java] view plain copy print?
  1. public class DragView extends RelativeLayout implements MoveLayout.DeleteMoveLayout{  
  2.   
  3.     public DragView(Context context) {  
  4.         super(context);  
  5.         init(context, this);  
  6.     }  
  7.   
  8.     private void init(Context c, DragView thisp) {  
  9.         mContext = c;  
  10.         mMoveLayoutList = new ArrayList<>();  
  11.     }  
  12. /* 
  13. *通过ondraw获取DragView的实际大小,然后告诉所有被管理的MoveLayout 
  14. */  
  15.     @Override  
  16.     protected void onDraw(Canvas canvas) {  
  17.         super.onDraw(canvas);  
  18.       //  Log.e(TAG, "onDraw: height=" + getHeight());  
  19.         mSelfViewWidth = getWidth();  
  20.         mSelfViewHeight = getHeight();  
  21.   
  22.         if (mMoveLayoutList != null) {  
  23.             int count = mMoveLayoutList.size();  
  24.             for (int a = 0; a < count; a ++) {  
  25.                 mMoveLayoutList.get(a).setViewWidthHeight(mSelfViewWidth, mSelfViewHeight);  
  26.                 mMoveLayoutList.get(a).setDeleteWidthHeight(DELETE_AREA_WIDTH, DELETE_AREA_HEIGHT);  
  27.             }  
  28.         }  
  29.     }  
  30.   
  31. //添加新的MoveLayout,并在里面放置自己的控件selfView,同时制定位置和尺寸  
  32.     public void addDragView(View selfView, int left,  int top ,int right, int bottom, boolean isFixedSize,  boolean whitebg) {  
  33.         MoveLayout moveLayout = new MoveLayout(mContext);  
  34.         moveLayout.setClickable(true);  
  35.         moveLayout.setMinHeight(mMinHeight);  
  36.         moveLayout.setMinWidth(mMinWidth);  
  37.         int tempWidth = right - left;  
  38.         int tempHeight = bottom - top;  
  39.         if (tempWidth < mMinWidth) tempWidth = mMinWidth;  
  40.         if (tempHeight < mMinHeight) tempHeight = mMinHeight;  
  41.   
  42.         //set postion  
  43.         RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(tempWidth, tempHeight);  
  44.         lp.setMargins(left,top,0,0);  
  45.         moveLayout.setLayoutParams(lp);  
  46.   
  47.         //add sub view (has click indicator)  
  48.         LayoutInflater inflater = LayoutInflater.from(mContext);  
  49.         View dragSubView = inflater.inflate(R.layout.drag_sub_view, null);  
  50.         LinearLayout addYourViewHere = (LinearLayout) dragSubView.findViewById(R.id.add_your_view_here);  
  51.         LinearLayout.LayoutParams lv = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);  
  52.         addYourViewHere.addView(selfView, lv);  
  53.   
  54.         moveLayout.addView(dragSubView);  
  55.         //set fixed size  
  56.         moveLayout.setFixedSize(isFixedSize);  
  57.   
  58.         addView(moveLayout);  
  59.         mMoveLayoutList.add(moveLayout);  
  60.     }  
  61. //添加新的MoveLayout,并在里面放置自己的控件selfView,同时制定位置和尺寸  
  62.     public void addDragView(int resId,  int left, int top ,int right, int bottom,  boolean isFixedSize, boolean whitebg) {  
  63.         LayoutInflater inflater2 = LayoutInflater.from(mContext);  
  64.         View selfView = inflater2.inflate(resId, null);  
  65.         addDragView(selfView, left, top , right, bottom, isFixedSize, whitebg);  
  66.     }  
  67.   
  68.     @Override  
  69.     public void onDeleteMoveLayout(int identity) {  
  70.         int count = mMoveLayoutList.size();  
  71.         for (int a = 0; a < count; a ++) {  
  72.             if (mMoveLayoutList.get(a).getIdentity() == identity) {  
  73.                 //delete  
  74.                 removeView(mMoveLayoutList.get(a));  
  75.             }  
  76.         }  
  77.     }  
  78.   
  79. }  
public class DragView extends RelativeLayout implements MoveLayout.DeleteMoveLayout{

    public DragView(Context context) {
        super(context);
        init(context, this);
    }

    private void init(Context c, DragView thisp) {
        mContext = c;
        mMoveLayoutList = new ArrayList<>();
    }
/*
*通过ondraw获取DragView的实际大小,然后告诉所有被管理的MoveLayout
*/
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
      //  Log.e(TAG, "onDraw: height=" + getHeight());
        mSelfViewWidth = getWidth();
        mSelfViewHeight = getHeight();

        if (mMoveLayoutList != null) {
            int count = mMoveLayoutList.size();
            for (int a = 0; a < count; a ++) {
                mMoveLayoutList.get(a).setViewWidthHeight(mSelfViewWidth, mSelfViewHeight);
                mMoveLayoutList.get(a).setDeleteWidthHeight(DELETE_AREA_WIDTH, DELETE_AREA_HEIGHT);
            }
        }
    }

//添加新的MoveLayout,并在里面放置自己的控件selfView,同时制定位置和尺寸
    public void addDragView(View selfView, int left, int top ,int right, int bottom, boolean isFixedSize, boolean whitebg) {
        MoveLayout moveLayout = new MoveLayout(mContext);
        moveLayout.setClickable(true);
        moveLayout.setMinHeight(mMinHeight);
        moveLayout.setMinWidth(mMinWidth);
        int tempWidth = right - left;
        int tempHeight = bottom - top;
        if (tempWidth < mMinWidth) tempWidth = mMinWidth;
        if (tempHeight < mMinHeight) tempHeight = mMinHeight;

        //set postion
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(tempWidth, tempHeight);
        lp.setMargins(left,top,0,0);
        moveLayout.setLayoutParams(lp);

        //add sub view (has click indicator)
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View dragSubView = inflater.inflate(R.layout.drag_sub_view, null);
        LinearLayout addYourViewHere = (LinearLayout) dragSubView.findViewById(R.id.add_your_view_here);
        LinearLayout.LayoutParams lv = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addYourViewHere.addView(selfView, lv);

        moveLayout.addView(dragSubView);
        //set fixed size
        moveLayout.setFixedSize(isFixedSize);

        addView(moveLayout);
        mMoveLayoutList.add(moveLayout);
    }
//添加新的MoveLayout,并在里面放置自己的控件selfView,同时制定位置和尺寸
    public void addDragView(int resId, int left, int top ,int right, int bottom, boolean isFixedSize, boolean whitebg) {
        LayoutInflater inflater2 = LayoutInflater.from(mContext);
        View selfView = inflater2.inflate(resId, null);
        addDragView(selfView, left, top , right, bottom, isFixedSize, whitebg);
    }

    @Override
    public void onDeleteMoveLayout(int identity) {
        int count = mMoveLayoutList.size();
        for (int a = 0; a < count; a ++) {
            if (mMoveLayoutList.get(a).getIdentity() == identity) {
                //delete
                removeView(mMoveLayoutList.get(a));
            }
        }
    }

}

六.     在自己项目里加入方法推荐

        本控件实现上非常简单,当然也稳定,并且显示控件定制化要求高,因此建议直接复制类到自己的工程里,并进行个性化的修改,因此这个控件没有做成库文件。整个控件的实现和调用工程。

自定义控件包括:MoveLayout.java,DragView,drag_sub_view.xml,corners_bg.xml,corners_bg2.xml,spot_corners_bg.xml

七.     下载

Github 下载链接
CSDN 下载链接