您的位置:首页 > 移动开发 > Android开发

自定义拖拽布局,gridlayout

2016-10-29 21:59 645 查看
1、这个自定义的拖拽布局是一次我在开发的时候,公司有一个需求,要点击一个按钮然后弹出一个对话框,对话框有一条分割线,分割线的上面是正在显示的在indecator上面的,下面的是没有显示的,点击上面的那么上面的标题就会跑到下面。那么indecator上面就会少一个标题。点击下面就跑到上面去,indecator就会增加一个标题,并且上面的是可以拖拽位子的,而indecatore上面显示的标题就是跟对话框上面部分的标题的顺序来排序的。。。。。。。。。。。。。。大概是这样一个需求。

2、我在gridlayout的基础上写了一个这个自定义控件,这里没有写出那个dialog,只是将这个功能大概的实现了。

3、自定义控件部门代码:=========================

/**
* 1、设置数据   public void setData(List<String> data)
* 2、设置是否能够拖拽 public void setAllowDrag(boolean isAllow)
* 3、向外部设置接口回调OnItemClickListener,自己去处理条目点击事件
* 4、让外界通过数据的形式添加一个View  ;public void addItem(String item)
* 之所以这样而不是直接addView,是因为那样会由于动画的原因而出现很多bug
*/

public class DragSortGridLayout extends GridLayout {

private boolean isAllow;
private View currentView;
private OnItemClickListener listener;

public static interface OnItemClickListener {
void onItemClick(View view, String text);
}

public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}

public DragSortGridLayout(Context context) {
this(context, null);
}

public DragSortGridLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public DragSortGridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
setColumnCount(4);
setOnDragListener(mOnDragListener);
LayoutTransition layoutTransition = new LayoutTransition();
layoutTransition.setDuration(150);
setLayoutTransition(layoutTransition);//设置布局动画
}

/**
* 这是外界设置数据进来,当然数据可以不是List<String>
*
* @param data
*/
public void setData(List<String> data) {
removeAllViews();
for (String title : data) {
addItem(title);
}
}

public void addItem(String title) {
TextView tv = new TextView(getContext());
tv.setText(title);
tv.setTextSize(18);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundResource(R.drawable.tv_bg);
tv.setOnClickListener(mOnClickListener);
tv.setOnLongClickListener(mOnLongClickListener);
setTVParams(tv);
addView(tv, 0);
}

private void setTVParams(TextView tv) {
int screenWidth = getResources().getDisplayMetrics().widthPixels;//获取屏幕的宽
GridLayout.LayoutParams lp = new GridLayout.LayoutParams();//获取gridlayout里面的孩子的属性
int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());//将4dp转换成像素
lp.width = screenWidth / getColumnCount() - 2 * margin;//设置孩子的宽,是平分屏幕,要减去两个margin
lp.height = GridLayout.LayoutParams.WRAP_CONTEN
150db
T;
lp.setMargins(margin, margin, margin, margin);
tv.setLayoutParams(lp);
}

public void setAllowDrag(boolean isAllow) {
this.isAllow = isAllow;
}

private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClick(v, ((TextView) v).getText().toString());
}
}
};

/**
* 长按监听,返回值为true,代表消费事件,如果返回值为false,那么即使你是长按了,当松手的时候,走的
* 还是点击事件的逻辑
*/
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
/**
* 这个自定义控件对外开放,那么就要对外提供,是都允许拖拽,如果外界不允许就直接返回true,
*/
if (!isAllow) {
return true;
}
//第二个参数是影子,就是你长按之后,手指可以带着动的那个影子,这个影子一个传任何东西,不一定非要是本身
v.startDrag(null, new View.DragShadowBuilder(v), null, 0);
currentView = v;
return true;
}
};

/**
* 对gridlayout的拖拽监听,返回值true代表接收拖拽产生的事件
* 总共有六种事件:
* (1)按下:STARTED
* (2)松手:DROP
* (3)结束:ENDED
* (4)拖拽的时候手指离开了gridlayout的区域:EXITED
* (5)拖拽的时候手指又回到了gridlayout的区域:ENTERED
* (6)在拖拽的时候,手指在gridlayouy的区域里面的坐标位置:LOCATION,原点坐标是gridlayout的左上方
*/
private View.OnDragListener mOnDragListener = new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
/**
* 由于返回值是整数,不好识别是那种事件类型,所以通过hashmap将对应的事件封装起来
*/
switch (event.getAction()) {
/**
* 在start是在按下的一瞬间开始的,在这一瞬间把所有的gridlayout的孩子放到一个数组里面
* 然后,当长按的时候,可以拖动的时候把当前拖拽的那个孩子移动到其他孩子的位置上的时候就
* 变动其他的孩子的位子。那么经过思考决定放在一个Rect数组里面,因为Recr对象有一个方法,可以
* 判断当前坐标是不是被包含在Rect里面,这样就可以方便移动位子
*/
case DragEvent.ACTION_DRAG_STARTED:
if (currentView != null) {
currentView.setBackgroundResource(R.drawable.tv_bg_disable);
}
initRects();
break;
/**
* 这个方法是在拖拽的位置在gridlayout的范围中移动的时候被调用,可以在这里判断当前拖拽的孩子
* “笼罩”在那个位置上
*/
case DragEvent.ACTION_DRAG_LOCATION:
int index = findTouchIndex(event.getX(), event.getY());
//这三个判断:一是判断有没有笼罩在其他孩子上面,二是排除笼罩的是自己本身
if (index >= 0 && getChildAt(index) != currentView) {
removeView(currentView);//移除本身
addView(currentView, index);//添加到笼罩的那个位置上
}
break;
case DragEvent.ACTION_DRAG_ENDED:
if (currentView != null) {
currentView.setBackgroundResource(R.drawable.tv_bg);
}
break;
}
return true;
}

private int findTouchIndex(float x, float y) {
for (int i = 0; i < rects.length; i++) {
if (rects[i].contains((int) x, (int) y)) {
return i;
}
}

return -1;//代表拖拽的孩子没有笼罩在任何其他孩子上面
}

Rect[] rects;

private void initRects() {
int childCount = getChildCount();
rects = new Rect[childCount];
for (int i = 0; i < rects.length; i++) {
View childAt = getChildAt(i);
rects[i] = new Rect(childAt.getLeft(), childAt.getTop(), childAt.getRight(), childAt.getBottom());

}
}
};

// SparseArray<String> 相当于 HashMap<Integer,String> 但更高效、谷歌官方推荐
static SparseArray<String> dragEventType = new SparseArray<String>();

static {
dragEventType.put(DragEvent.ACTION_DRAG_STARTED, "STARTED");
dragEventType.put(DragEvent.ACTION_DRAG_ENDED, "ENDED");
dragEventType.put(DragEvent.ACTION_DRAG_ENTERED, "ENTERED");
dragEventType.put(DragEvent.ACTION_DRAG_EXITED, "EXITED");
dragEventType.put(DragEvent.ACTION_DRAG_LOCATION, "LOCATION");
dragEventType.put(DragEvent.ACTION_DROP, "DROP");
}

public static String getDragEventAction(DragEvent de) {
return dragEventType.get(de.getAction());
}
}


4、mainActivity的xml  ===============》

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<shen.da.ye.draglayoutdemo.DragSortGridLayout
android:id="@+id/gridlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"></shen.da.ye.draglayoutdemo.DragSortGridLayout>

<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="#615e5e" />

<shen.da.ye.draglayoutdemo.DragSortGridLayout
android:id="@+id/hind_gridlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"></shen.da.ye.draglayoutdemo.DragSortGridLayout>
</LinearLayout>


5、mainActivity的代码 =================》

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final DragSortGridLayout gridlayout = (DragSortGridLayout) findViewById(R.id.gridlayout);
final DragSortGridLayout hindGridLayout = (DragSortGridLayout) findViewById(R.id.hind_gridlayout);
List<String> list = new ArrayList<>();
list.add("刘德华");
list.add("霍建华");
list.add("胡歌");
list.add("苏有朋");
list.add("林志颖");
list.add("吴奇隆");
list.add("小智");
list.add("布兰德");
list.add("艾希");
list.add("提莫");
list.add("石头人");
list.add("女警");
gridlayout.setData(list);
gridlayout.setAllowDrag(true);
gridlayout.setOnItemClickListener(new DragSortGridLayout.OnItemClickListener() {
@Override
public void onItemClick(View view, String text) {
gridlayout.removeView(view);
hindGridLayout.addItem(text);
}
});

hindGridLayout.setData(list);
hindGridLayout.setOnItemClickListener(new DragSortGridLayout.OnItemClickListener() {
@Override
public void onItemClick(View view, String text) {
hindGridLayout.removeView(view);
gridlayout.addItem(text);
}
});
}
}


6、两个drawable =====================>

  (1)tv_bg,也就是正常情况下的textview的背景:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="1dp" android:color="#000"/>
<corners android:radius="5dp"/>
</shape>


 (2)tv_bg_disable:也就是长按的情况下textview的背景:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="#0f0" android:dashGap="2dp" android:dashWidth="7dp" android:width="1dp"/>
<corners android:radius="5dp" />
</shape>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息