最近看了视频学习了类似网易的频道拖动效果,就跟着敲了几遍,下面是总结和记录:
先看效果图,下图看不到的话,点击这里看效果
实现的效果:
1.点击按钮添加频道(gif没显示出来,但是有这个功能哈~)
2.长按按钮,实现textView的拖拽,原来的位置要有一个虚线为底的textview
3.当拖拽放到某个位置时,原来的位置的textVIew要删除掉,拖拽的textView放入当前位置。textView背景色恢复。
具体代码:代码中已经有较为详细的说明了,直接上~
java文件:
public class MyStuActivity extends AppCompatActivity {
private Button button;
private GridLayout layout;
private View selectView;
private List<Rect> rects;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_stu);
button = findViewById(R.id.btn);
layout = findViewById(R.id.layout_myGrid);
/**
* 在gridlayout中做拖拽监听
*/
layout.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
/**
* 开始拖拽,初始化矩形,这里面的每个矩形就是初始textview的位置,
* 利用矩形判断当前坐标是否在矩形内,如果再矩形内,就删除原来的textview,
* 并在当前位置添加拖拽的textView
*/
initRects();
break;
case DragEvent.ACTION_DRAG_LOCATION:
//此部分是拖拽时的事件,只要在拖拽,就会一直响应此事件
int pos = isConstans(event);
if (pos > -1 && selectView != null && selectView != layout.getChildAt(pos)) {
//先删除原来的view
layout.removeView(selectView);
//添加显得veiw
layout.addView(selectView, pos);
}
break;
case DragEvent.ACTION_DRAG_ENDED:
//拖拽结束必定发生的事件,设置textview的背景
selectView.setEnabled(true);
break;
}
return true;
}
});
}
/**
* 是否包含
*
* @param event
* @return
*/
private int isConstans(DragEvent event) {
for (int i = 0; i < rects.size(); i++) {
if (rects.get(i).contains((int) event.getX(), (int) event.getY())) {
return i;
}
}
return -1;
}
//初始化矩形
private void initRects() {
rects = new ArrayList<>();
for (int i = 0; i < layout.getChildCount(); i++) {
Rect rect = new Rect(layout.getChildAt(i).getLeft(), layout.getChildAt(i).getTop(),
layout.getChildAt(i).getRight(), layout.getChildAt(i).getBottom());
rects.add(rect);
}
}
//btn响应事件,点击添加一个textview
public void addMyItem(final View view) {
//添加一個子項
int margin = 10;
final TextView textView = new TextView(MyStuActivity.this);
textView.setText("新闻");
textView.setTextColor(getResources().getColor(R.color.colorBlack));
textView.setGravity(Gravity.CENTER);
//背景是一个选择器
textView.setBackground(getResources().getDrawable(R.drawable.selector_tv));
textView.setTextSize(20);
textView.setPadding(margin, margin, margin, margin);
//设置宽、高、margin时使用的layoutParams,注意此处的layoutParams必须是其父布局类型的layoutParams,
//即必须使用GridLayout.LayoutParams
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams();
layoutParams.setMargins(margin, margin, margin, margin);
layoutParams.height = GridLayout.LayoutParams.WRAP_CONTENT;
layoutParams.width = getResources().getDisplayMetrics().widthPixels / 4 - 2 * margin;
layoutParams.setMargins(margin, margin, margin, margin);
textView.setLayoutParams(layoutParams);
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
/**
* startDragAndDrop就是view的拖拽方法,他主要做了生成一个跟随鼠标拖动的控件视图。
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.startDragAndDrop(null, new View.DragShadowBuilder(v),
null, 0);
} else {
textView.startDrag(null, new View.DragShadowBuilder(v),
null, 0);
}
selectView = v;
selectView.setEnabled(false);
return false;
}
});
layout.addView(textView, 0);
}
}
xml内容:主要是设置gridlayout中列数,已经显示动画效果:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="app.study.com.mypindaostu20190123.MyStuActivity">
<Button
android:id="@+id/btn"
android:text="添加"
android:onClick="addMyItem"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<GridLayout
android:columnCount="4"
android:animateLayoutChanges="true"
android:id="@+id/layout_myGrid"
android:layout_width="match_parent"
android:layout_height="wrap_content"></GridLayout>
</LinearLayout>
textView的选择器代码:主要是设置了textView可不可用的属性,当可用/不可用的时候的背景
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:drawable="@drawable/shape_tv_nor"/>
<item android:state_enabled="false" android:drawable="@drawable/shape_tv_sel"/>
</selector>
不用情况下的背景差不多,只是不可用的时候,背景边框是虚线而已,这里只贴下虚线的显示:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="#ffffff" />
<!--dashGap是虚线的间隔-->
<!--dashWidth是虚线的宽-->
<stroke
android:width="1dp"
android:color="#f00"
android:dashGap="2dp"
android:dashWidth="1dp" />
</shape>
至此,初步完成,不知道为啥/**/的注释会变形,真心丑,还重新试了一下,还是老样子。。。
最后将其简单封装成类:
public class DragGridLayout extends GridLayout {
//设置列数,默认4
private int columnCount = 4;
//控制是否长按可以拖拽
private boolean canDrag;
//被点击拖拽的视图
private View selectView;
//为每个textview画一个矩形
private List<Rect> rects;
public DragGridLayout(Context context) {
this(context, null);
}
public DragGridLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragGridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initGridLayout();
}
//初始化gridlayout
private void initGridLayout() {
//设置最大列数啊
this.setColumnCount(columnCount);
//设置动画
this.setLayoutTransition(new LayoutTransition());
}
//添加数据
public void addItem(List<String> datas) {
for (String data : datas) {
//创建新的textView,将其添加进父布局中
DragGridLayout.this.addView(newItem(data));
}
}
//创建一个新的textview
private TextView newItem(String data) {
final TextView textView = new TextView(getContext());
//设置边距
int margin = 10;
//设置textView的内容
textView.setText(data);
//设置文字颜色
textView.setTextColor(getResources().getColor(R.color.colorBlack));
//文字居中
textView.setGravity(Gravity.CENTER);
//背景是一个选择器
textView.setBackground(getResources().getDrawable(R.drawable.selector_tv));
//文字大小
textView.setTextSize(20);
//内边距
textView.setPadding(margin, margin, margin, margin);
//设置宽、高、margin时使用的layoutParams,注意此处的layoutParams必须是其父布局类型的layoutParams,
//即必须使用GridLayout.LayoutParams
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams();
layoutParams.setMargins(margin, margin, margin, margin);
layoutParams.height = GridLayout.LayoutParams.WRAP_CONTENT;
layoutParams.width = getResources().getDisplayMetrics().widthPixels / 4 - 2 * margin;
layoutParams.setMargins(margin, margin, margin, margin);
textView.setLayoutParams(layoutParams);
if (this.canDrag) {
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
/**
* startDragAndDrop就是view的拖拽方法,他主要做了生成一个跟随鼠标拖动的控件视图。
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.startDragAndDrop(null, new View.DragShadowBuilder(v),
null, 0);
} else {
textView.startDrag(null, new View.DragShadowBuilder(v),
null, 0);
}
selectView = v;
selectView.setEnabled(false);
return false;
}
});
} else {
textView.setOnLongClickListener(null);
}
return textView;
}
//初始化矩形坐标
private void initRects() {
rects = new ArrayList<>();
for (int i = 0; i < DragGridLayout.this.getChildCount(); i++) {
Rect rect = new Rect(DragGridLayout.this.getChildAt(i).getLeft(), DragGridLayout.this.getChildAt(i).getTop(),
DragGridLayout.this.getChildAt(i).getRight(), DragGridLayout.this.getChildAt(i).getBottom());
//添加
rects.add(rect);
}
}
/**
* 是否包含
*
* @param event
* @return
*/
private int isConstans(DragEvent event) {
for (int i = 0; i < rects.size(); i++) {
if (rects.get(i).contains((int) event.getX(), (int) event.getY())) {
return i;
}
}
return -1;
}
//设置是否可以长按拖拽
public void setAllowDrag(boolean canDrag) {
this.canDrag = canDrag;
if (canDrag) {
DragGridLayout.this.setOnDragListener(new OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_ENDED:
selectView.setEnabled(true);
break;
case DragEvent.ACTION_DRAG_STARTED:
initRects();
break;
case DragEvent.ACTION_DRAG_LOCATION:
//判断是否在当前的矩形内部
int pos = isConstans(event);
if (pos > -1 && selectView != null && selectView != DragGridLayout.this.getChildAt(pos)) {
//删除
DragGridLayout.this.removeView(selectView);
//添加
DragGridLayout.this.addView(selectView, pos);
}
break;
}
//此处需要返回true,说明成功处理了拖拽事件;
/**
* @return {@code true} if the drag event was handled successfully, or {@code false}
* if the drag event was not handled. Note that {@code false} will trigger the View
* to call its {@link #onDragEvent(DragEvent) onDragEvent()} handler.
*/
return true;
}
});
} else {
DragGridLayout.this.setOnDragListener(null);
}
}
}
在java文件中的调用就很简单了:
dragGridLayout = (DragGridLayout) findViewById(R.id.layout_draggridlayout);
List<String> datas = new ArrayList<>();
datas.add("新闻");
datas.add("娱乐");
datas.add("体育");
datas.add("科技");
datas.add("社会");
datas.add("明星");
datas.add("八卦");
datas.add("军事");
datas.add("游戏");
datas.add("时尚");
datas.add("购物");
datas.add("推文");
//setAllowDrag需要在addItem前面设置!
dragGridLayout.setAllowDrag(true);
dragGridLayout.addItem(datas);
封装后的效果就是第一图的效果,没有button的。实现了textView的拖拽。