项目中需要有listview侧拉出菜单,点击可以置顶,撤销,暂停,删除的功能 在此记录一下。
需求是侧拉可以拉出菜单,然后点击侧拉菜单的每个条目有相应的点击事件处理,而且再次侧拉的图标需要改变,再加上上拉加载更多,下拉刷新。安卓原生的listview不像苹果的tableview一样自带侧拉接口,所有肯定需要自定义控件了,所以刚开始试了3种实现方式
先看效果图(这是3的效果图)
demo地址
1.第一种就是把listview的item布局设置成paddingleft或者paddingright是负的,然后继承listview 重写onTouchEvent方法,然在move时候判断x坐标变化,以此来判断左滑还是右滑,然后滑出来的点击事件在adapter里操作,因为我侧拉置顶或者别的需要点击事件继续请求服务器,实际操作中很麻烦而且换不了侧拉后的图标,所以planA放弃了
package com.hxzh.uniwill.lingjian.View;
/**
* Created by pang on 2017/3/30.
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Scroller;
/**
* 侧向滑出菜单的ListView
* 使用请注意与ListView的Item的布局配合,
* 该效果的实现是基于在Item的布局中通过设置PaddingLeft和PaddingRight来隐藏左右菜单的,
* 所以使用此ListView时,请务必在布局Item时使用PaddingLeft和PaddingRight;
* 或者自己改写此ListView,已达到想要的实现方式
*
*/
public class SlideListView2 extends ListView {
/**禁止侧滑模式*/
public static int MOD_FORBID = 0;
/**从左向右滑出菜单模式*/
public static int MOD_LEFT = 1;
/**从右向左滑出菜单模式*/
public static int MOD_RIGHT = 2;
/**左右均可以滑出菜单模式*/
public static int MOD_BOTH = 3;
/**当前的模式*/
private int mode = MOD_FORBID;
/**左侧菜单的长度*/
private int leftLength = 0;
/**右侧菜单的长度*/
private int rightLength = 0;
/**
* 当前滑动的ListView position
*/
private int slidePosition;
/**
* 手指按下X的坐标
*/
private int downY;
/**
* 手指按下Y的坐标
*/
private int downX;
/**
* ListView的item
*/
private View itemView;
/**
* 滑动类
*/
private Scroller scroller;
/**
* 认为是用户滑动的最小距离
*/
private int mTouchSlop;
/**
* 判断是否可以侧向滑动
*/
private boolean canMove = false;
/**
* 标示是否完成侧滑
*/
private boolean isSlided = false;
/**后加
* 速度追踪对象
*/
private VelocityTracker velocityTracker;
private static final int SNAP_VELOCITY = 600;
/**后加
* 是否响应滑动,默认为不响应
*/
private boolean isSlide = false;
private OnDeleteListener listener;
public SlideListView2(Context context) {
this(context, null);
}
public SlideListView2(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideListView2(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
scroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public void setOnDeleteListener(OnDeleteListener l) {
listener = l;
}
/**
* 初始化菜单的滑出模式
* @param mode
*/
public void initSlideMode(int mode){
this.mode = mode;
}
/**
* 后加+
* 分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
System.out.println("dispatch-->" + "down");
addVelocityTracker(event);
// 假如scroller滚动还没有结束,我们直接返回
if (!scroller.isFinished()) {
return false;
}
downX = (int) event.getX();
downY = (int) event.getY();
slidePosition = pointToPosition(downX, downY);
// 无效的position, 不做任何处理
if (slidePosition == AdapterView.INVALID_POSITION) {
return super.dispatchTouchEvent(event);
}
// 获取我们点击的item view
itemView = getChildAt(slidePosition - getFirstVisiblePosition());
break;
}
case MotionEvent.ACTION_MOVE: {
System.out.println("dispatch-->" + "move");
if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY
|| (Math.abs(event.getX() - downX) > mTouchSlop && Math
.abs(event.getY() - downY) < mTouchSlop)) {
isSlide = true;
}
break;
}
case MotionEvent.ACTION_UP:
recycleVelocityTracker();
break;
}
return super.dispatchTouchEvent(event);
}
/**
* 处理我们拖动ListView item的逻辑
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
int lastX = (int) ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
System.out.println("touch-->" + "down");
/*当前模式不允许滑动,则直接返回,交给ListView自身去处理*/
if(this.mode == MOD_FORBID){
return super.onTouchEvent(ev);
}
// 如果处于侧滑完成状态,侧滑回去,并直接返回
if (isSlided) {
scrollBack();
return false;
}
// 假如scroller滚动还没有结束,我们直接返回
if (!scroller.isFinished()) {
return false;
}
downX = (int) ev.getX();
downY = (int) ev.getY();
slidePosition = pointToPosition(downX, downY);
// 无效的position, 不做任何处理
if (slidePosition == AdapterView.INVALID_POSITION) {
return super.onTouchEvent(ev);
}
// 获取我们点击的item view
itemView = getChildAt(slidePosition - getFirstVisiblePosition());
/*此处根据设置的滑动模式,自动获取左侧或右侧菜单的长度*/
if(this.mode == MOD_BOTH){
this.leftLength = -itemView.getPaddingLeft();
this.rightLength = -itemView.getPaddingRight();
}else if(this.mode == MOD_LEFT){
this.leftLength = -itemView.getPaddingLeft();
}else if(this.mode == MOD_RIGHT){
this.rightLength = -itemView.getPaddingRight();
}
break;
case MotionEvent.ACTION_MOVE:
System.out.println("touch-->" + "move");
if (!canMove
&& slidePosition != AdapterView.INVALID_POSITION
&& (Math.abs(ev.getX() - downX) > mTouchSlop && Math.abs(ev
.getY() - downY) < mTouchSlop)) {
int offsetX = downX - lastX;
if(offsetX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){
/*从右向左滑*/
canMove = true;
}else if(offsetX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){
/*从左向右滑*/
canMove = true;
}else{
canMove = false;
}
/*此段代码是为了避免我们在侧向滑动时同时触发ListView的OnItemClickListener时间*/
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent
.setAction(MotionEvent.ACTION_CANCEL
| (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
onTouchEvent(cancelEvent);
}
if (canMove) {
/*设置此属性,可以在侧向滑动时,保持ListView不会上下滚动*/
requestDisallowInterceptTouchEvent(true);
// 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚
int deltaX = downX - lastX;
if(deltaX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){
/*向左滑*/
itemView.scrollTo(deltaX, 0);
}else if(deltaX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){
/*向右滑*/
itemView.scrollTo(deltaX, 0);
}else{
itemView.scrollTo(0, 0);
}
return true; // 拖动的时候ListView不滚动
}
case MotionEvent.ACTION_UP:
System.out.println("touch-->" + "up");
if (canMove){
canMove = false;
scrollByDistanceX();
}
break;
}
// 否则直接交给ListView来处理onTouchEvent事件
return super.onTouchEvent(ev);
}
/**
* 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动
*/
private void scrollByDistanceX() {
/*当前模式不允许滑动,则直接返回*/
if(this.mode == MOD_FORBID){
return;
}
if(itemView.getScrollX() > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){
/*从右向左滑*/
if (itemView.getScrollX() >= rightLength / 2) {
scrollLeft();
} else {
// 滚回到原始位置
scrollBack();
}
}else if(itemView.getScrollX() < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){
/*从左向右滑*/
if (itemView.getScrollX() <= -leftLength / 2) {
scrollRight();
} else {
// 滚回到原始位置
scrollBack();
}
}else{
// 滚回到原始位置
scrollBack();
}
}
/** 后加+
* 添加用户的速度跟踪器
*
* @param event
*/
private void addVelocityTracker(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
}
/**后加+
* 移除用户速度跟踪器
*/
private void recycleVelocityTracker() {
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
/**后加+
* 获取X方向的滑动速度,大于0向右滑动,反之向左
*
* @return
*/
private int getScrollVelocity() {
velocityTracker.computeCurrentVelocity(1000);
int velocity = (int) velocityTracker.getXVelocity();
return velocity;
}
/**
* 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值
*/
private void scrollRight() {
isSlided = true;
final int delta = (leftLength + itemView.getScrollX());
// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,
Math.abs(delta));
postInvalidate(); // 刷新itemView
}
/**
* 向左滑动,根据上面我们知道向左滑动为正值
*/
private void scrollLeft() {
isSlided = true;
final int delta = (rightLength - itemView.getScrollX());
// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
scroller.startScroll(itemView.getScrollX(), 0, delta, 0,
Math.abs(delta));
postInvalidate(); // 刷新itemView
}
/**
* 滑动会原来的位置
*/
private void scrollBack() {
isSlided = false;
scroller.startScroll(itemView.getScrollX(), 0, -itemView.getScrollX(),
0, Math.abs(itemView.getScrollX()));
postInvalidate(); // 刷新itemView
}
@Override
public void computeScroll() {
// 调用startScroll的时候scroller.computeScrollOffset()返回true,
if (scroller.computeScrollOffset()) {
// 让ListView item根据当前的滚动偏移量进行滚动
itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
/**
* 提供给外部调用,用以将侧滑出来的滑回去
*/
public void slideBack() {
this.scrollBack();
}
public interface OnDeleteListener {
void onDelete(int index);
void onTop(int index);
}
}
2.还是从item角度考虑,把item当作一个viewgroup,耦合性最低,没有冲突,滑动体验非常的棒,类似与iOS侧拉的阻塞式体验,也就是侧拉时候禁止上拉下拉,但是实际操作中并不能动态更换图标,所有也放弃了planB(控件没有问题,可能我自己没解决好)
这里是开源控件的地址SwipeDelMenuLayout
3.然后就选择了开源的控件使用
感谢侧拉listview控件SwipeMenuListView
3.1
compile 'com.baoyz.swipemenulistview:library:1.3.0'
布局
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_below="@+id/titlefragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.baoyz.swipemenulistview.SwipeMenuListView
android:id="@+id/swiplistView"
android:cacheColorHint="#00000000"
android:listSelector="#00000000"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
下拉刷新用的是v4包自带的swiperefreshlayout
3.2:然后先看adapter
//这里根据置顶字段来区别position
@Override
public int getItemViewType(int position) {
int type = -1;
data = list2.get(position);
iszhiding = data.getStick();//置顶字段
switch (iszhiding) {
case 0:
type = 0;
break;
case 1:
type = 1;
break;
}
return type;
}
//返回条目类型,侧拉置顶,侧拉取消置顶,-1
@Override
public int getViewTypeCount() {
return 3;
}
就是正常的写法 ,这里是加了两个方法,就是实现多条目布局,getitemviewtype返回条目类型,getviewtypecount返回条目类型个数,然后侧拉出来置顶,和再次侧拉出来取消置顶,两个显示图标其实就是两种条目类型,根据服务器字段显示置顶或是取消置顶的图标,我这里只写了一种菜单,比如侧拉菜单要是多个,比如三个的话,就是6种条目类型,然后根据返回字段判断对应显示的图标,
3.3:然后用代码构造侧拉的菜单,可以设置图标,还有宽度
//侧拉swipmenulistview 构建侧拉菜单
SwipeMenuCreator creator1 = new SwipeMenuCreator() {
@Override
public void create(SwipeMenu menu) {
switch (menu.getViewType()) {
case 0:
createMenu1(menu);
break;
case 1:
createMenu2(menu);
break;
}
}
private void createMenu1(SwipeMenu menu) {
//创建置顶项
SwipeMenuItem zditem1 = new SwipeMenuItem(MainActivity.this);
//设置item width
zditem1.setWidth(dp2px(60));
//设置背景
zditem1.setBackground(R.color.listview_item_right_bg);
//图标
zditem1.setIcon(R.drawable.list_slide_but_stick2x);
// 添加到菜单
menu.addMenuItem(zditem1);
}
private void createMenu2(SwipeMenu menu) {
//创建置顶项
SwipeMenuItem zditem2 = new SwipeMenuItem(MainActivity.this);
//设置item width
zditem2.setWidth(dp2px(60));
//设置背景
zditem2.setBackground(R.color.listview_item_right_bg);
//图标
zditem2.setIcon(R.drawable.list_slide_but_canstick2x);
// 添加到菜单
menu.addMenuItem(zditem2);
}
};
然后设置侧拉菜单的点击事件
//设置创建者
swiplistView.setMenuCreator(creator1);
//step 2. listener item click event
swiplistView.setOnMenuItemClickListener(new SwipeMenuListView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
data1 = list1.get(position);
taskid = data1.getTaskid();
iszhiding1 = data1.getStick();//置顶
//显示置顶
if (iszhiding1 == 0) {
try {
OkHttpUtils.post(zhiding)
.params("userid", userid)
.params("taskid", taskid)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
Data_uploadBack_tag back_tag = JsonUtil.parseJsonToBean(s, Data_uploadBack_tag.class);
if (back_tag != null) {
// ToastUtil.showToast("置顶");
Toast.makeText(MainActivity.this,"置顶",Toast.LENGTH_SHORT).show();
showmore();//置顶服务器排序后从新获取数据刷新页面
} else {
// ToastUtil.showToast("置顶失败");
Toast.makeText(MainActivity.this,"置顶失败",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(Call call, Response response, Exception e) {
super.onError(call, response, e);
// ToastUtil.showToast("置顶失败,头皮发麻");
Toast.makeText(MainActivity.this,"置顶失败,头皮发麻",Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
e.printStackTrace();
}
} else if (iszhiding1 == 1) {
try {
OkHttpUtils.post(quxiaozd)
.params("userid", userid)
.params("taskid", taskid)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
Data_uploadBack_tag back_tag = JsonUtil.parseJsonToBean(s, Data_uploadBack_tag.class);
if (back_tag != null) {
// ToastUtil.showToast("取消置顶");
Toast.makeText(MainActivity.this,"取消置顶",Toast.LENGTH_SHORT).show();
showmore();
} else {
// ToastUtil.showToast("取消失败");
Toast.makeText(MainActivity.this,"取消失败",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(Call call, Response response, Exception e) {
super.onError(call, response, e);
Toast.makeText(MainActivity.this,"联网失败",Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
});
上面代码大体就是根据点位置响应点击事件,然后联网,告诉服务器是那个条目被置顶或者别的操作,服务器会重新排序,这是后根据回调,然后重新加载页面,刷新,这样置顶就变成了取消置顶,同时图标也更换了,而且条目位置也会有所改变(这里服务器排序,并非手动置顶)
手动:可以通过修改对应集合数据源,根据侧拉的potion,添加到第一个索引,然后移除当前位置条目,然后刷新adapter,删除的话就直接集合rmove掉对应索引数据然后刷新
这里还有一点需要注意,通过隐藏下拉控件解决侧拉和上拉的冲突
//解决listview与SwipeRefreshLayout下拉滑动和侧拉冲突问题
swiplistView.setOnMenuStateChangeListener(new SwipeMenuListView.OnMenuStateChangeListener() {
@Override
public void onMenuOpen(int position) {
swipeRefreshLayout.setEnabled(false);
}
@Override
public void onMenuClose(int position) {
}
});
下拉和listview下拉冲突
mListView1.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
int lastposition = mListView1.getLastVisiblePosition();//最后一个item的位置
// if (lastposition == mListView1.getCount() - 1) {
// isloadMore = true;
// footview.setPadding(0, 0, 0, footheight);
// Handler handler = new Handler();
// handler.postDelayed(new Runnable() {
@Override
// public void run() {
// onLoadMore();
// footview.setPadding(0, -footheight, 0, 0);
// ToastUtil.showToast("加载完成");
// }
// }, 2000);
// }
// }
if (mListView1 != null && mListView1.getChildCount() > 0
&&mListView1.getFirstVisiblePosition() == 0
&&mListView1.getChildAt(0).getTop() >=mListView1.getPaddingTop()) {
swipeRefreshLayout.setEnabled(true);
}else {
swipeRefreshLayout.setEnabled(false);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
boolean enable = false;
if (mListView1 != null && mListView1.getChildCount() > 0) {
// check if the first item of the list is visible
boolean firstItemVisible = mListView1.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = mListView1.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
swipeRefreshLayout.setEnabled(enable);
}
});
3.4:然后就是上拉加载更多和下拉刷新
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_green_light,
android.R.color.holo_orange_light);
//下拉刷新
try {
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
showmore();
Toast.makeText(MainActivity.this,"刷新完成",Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
},2000);
}
});
} catch (Exception e) {
e.printStackTrace();
}
联网操作
//下拉刷新
private void showmore() {
//联网刷新listview
try {
OkHttpUtils.get(Sever)
.params("pn", 1)
.params("size", sizeup)
.params("userid", userid)
.params("status", 3)
.params("sort", 1)
.params("type",1)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
shoudaorenwu = JsonUtil.parseJsonToBean(s, Data_chaxunliebiao_shoudaorenwu.class);
if (shoudaorenwu != null) {
if (list1 != null) {//还有数据
list1.clear();
}
list1 = shoudaorenwu.getList();
slideAdapter1 = new SlideAdapter1(MainActivity.this, list1);
swiplistView.setAdapter(slideAdapter1);
slideAdapter1.notifyDataSetChanged();
}else {
Toast.makeText(MainActivity.this,"对不起,没有最新数据了",Toast.LENGTH_SHORT).show();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
上拉就是添加脚布局
开始设成负的,拉的时候判断是否是底部,是就显示脚布局,然后分页加载,加载完,隐藏脚布局
footview = View.inflate(MainActivity.this, R.layout.listview_footer, null);
footview.measure(0, 0);
footheight = footview.getMeasuredHeight();
// bootm = dp2px(45);
footview.setPadding(0, -footheight, 0, 0);
swiplistView.addFooterView(footview);
swiplistView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
int lastposition = swiplistView.getLastVisiblePosition();//最后一个item的位置
if (lastposition == swiplistView.getCount() - 1) {
footview.setPadding(0, 0, 0, footheight);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
onLoadMore();
footview.setPadding(0, -footheight, 0, 0);
// Toast.makeText(MainActivity.this,"加载完成",Toast.LENGTH_SHORT).show();
}
}, 2000);
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
boolean enable = false; //这里做判断,只有滑到顶部才出来下拉进度条
if (swiplistView != null && swiplistView.getChildCount() > 0) {
// check if the first item of the list is visible
boolean firstItemVisible = swiplistView.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = swiplistView.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
swipeRefreshLayout.setEnabled(enable);
}
});
/**上拉加载更多
*
* 分页加载主要逻辑
* 两个集合,一个用来接收最新的数据,他是小集合,因为每次联网对集合长度做了限制,最多10条,
* 然后服务器有单独一个字段是服务器总共有多少数据,每次把小集合addll到大集合里,
* 再判断大集合的长度是否小于服务器返回总条目数,小于就继续添加,等于或大于,就提示没有更多数据了
*
*
* */
int m = 1;
private void onLoadMore() {
//联网刷新listview
m++;
try {
OkHttpUtils.get(Sever)
.params("pn", m)
.params("size", sizedown)
.params("userid", userid)
.params("status", 3)
.params("sort", 1)
.params("type",1)
.execute(new StringCallback() {
@Override
public void onSuccess(String s, Call call, Response response) {
shoudaorenwu = JsonUtil.parseJsonToBean(s, Data_chaxunliebiao_shoudaorenwu.class);
if (shoudaorenwu.getList()!= null){
list2.clear();
}
list2 = shoudaorenwu.getList();
int totle = shoudaorenwu.getTotal();//服务器返回总数
int totle2 = list1.size();//加载的数据
if (totle2 < totle) {
list1.addAll(list2);
slideAdapter1.notifyDataSetChanged();
} else {
m = 1;
// Toast.makeText(MainActivity.this,"没有更多数据了",Toast.LENGTH_SHORT).show();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
最后 感谢阅读
demo地址