Android拖動,縮放,自定義內容,控制元件製作(可拖動縮放RelativeLayout定製)
先上效果圖:
一. 製作此控制元件的起源
專案需要一個可以拖動的控制元件,在網上可以找到很多例子,有圖片拖動控制元件,有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,然後編寫邏輯程式碼:核心程式碼如下(都貼上上看著心累):
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:簡略程式碼如下:
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
七. 下載