自定义view-仿qq侧滑菜单的显示和删除
程序员文章站
2022-05-15 18:29:01
...
首先看下qq的截图和项目的效果(做的丑,习惯就好,毕竟我也很绝望)
1.定义布局:这里主布局我还是使用的ListView,大家可以换成RecylerView,主布局就不写了,其次就是这个每个item的布局,我定义了三个布局,第一个是联系人布局(TextView),第二个是删除布局(TextView),第三个用一个FrameLayout将两个合在一起
item_content
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="联系人"
android:textColor="#fff"
android:textSize="25sp">
</TextView>
item_menu
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:background="#ff0000"
android:gravity="center"
android:text="删除"
android:textColor="#fff"
android:textSize="25sp">
</TextView>
item_main:这里后期将FrameLayout换成自定义的布局,就不写了
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp">
<include
android:id="@+id/item_content"
layout="@layout/item_content" />
<include
android:id="@+id/item_menu"
layout="@layout/item_menu" />
</FrameLayout>
这时的布局是这样的
2.自定义布局SlideLayout继承FrameLayout,正常初始化显示item
得到子view对象
/**
* 布局加载完成回调改方法
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
contentView = getChildAt(0);
menuView = getChildAt(1);
}
得到子view的宽和高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
contentWidth = contentView.getMeasuredWidth();
menuWidth = menuView.getMeasuredWidth();
viewHeight = getMeasuredHeight();
}
对子view进行重新布局
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
menuView.layout(contentWidth, 0, contentWidth + menuWidth, viewHeight);
}
通过手势拖动打开或者关闭menu
实现左右滑动
/**
* 第一次按下的值
*/
private int lastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
int eventX = (int) event.getRawX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//1.记录起始坐标
lastX = eventX;
break;
case MotionEvent.ACTION_MOVE:
//2.计算偏移量
int distencX = eventX - lastX;
int toScrollX = getScrollX() - distencX;
System.out.println(toScrollX);
//屏蔽非法值
if (toScrollX < 0) {
toScrollX = 0;
}else if(toScrollX > menuWidth)
// scrollTo(toScrollX ,getScrollY)
sscrollTo(toScrollX, getScrollY());
//重新付值
lastX = eventX;
case MotionEvent.ACTION_U break;
}
// return super.onTouchEvent(event)
return true;
}
判断平滑打开还是关闭
case MotionEvent.ACTION_UP:
int totalScrollX = getScrollX();
if (totalScrollX < menuWidth / 2) {
//关闭
closeMenu();
} else {
//打开
openMenu();
}
break;
//打开菜单
public void openMenu() {
int distanceX = menuWidth - getScrollX();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
//强制刷新
invalidate();
if(onStateChangeListenter!=null){
onStateChangeListenter.onOpen(this);
}
}
//关闭菜单
public void closeMenu() {
int distanceX = 0 - getScrollX();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
//强制刷新
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
int currX = scroller.getCurrX();
scrollTo(currX, scroller.getCurrY());
invalidate();
}
}
在listview中显示item
private ListView lv_main;
private ArrayList<MyBean> myBeans;
private MyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_main = (ListView) findViewById(R.id.lv_main);
//设置适配器
//准备数据
myBeans = new ArrayList<>();
for (int i = 0; i < 100; i++) {
myBeans.add(new MyBean("联系人" + i));
}
myAdapter = new MyAdapter();
lv_main.setAdapter(myAdapter);
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return myBeans.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = View.inflate(MainActivity.this, R.layout.item_main, null);
viewHolder = new ViewHolder();
viewHolder.item_content = (TextView) convertView.findViewById(R.id.item_content);
viewHolder.item_menu = (TextView) convertView.findViewById(R.id.item_menu);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
//根据位置得到内容
final MyBean myBean = myBeans.get(position);
viewHolder.item_content.setText(myBean.getName());
viewHolder.item_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyBean myBean1 = myBeans.get(position);
Toast.makeText(MainActivity.this, myBean1.getName(), Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
}
static class ViewHolder {
TextView item_content;
TextView item_menu;
}
解决item滑动后不能自动关闭和打开
原因:事件被 ListView 拦截,也就是说,当前 ListView 与子 item 的冲突 反拦截
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//1.按下记录坐标
downX = startX = event.getX();
downY = startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
//2.按下记录结束坐标
float endX = event.getX();
float endY = event.getY();
float distance = endX - startX;
int toScrollX = (int) (getScrollX() - distance);
if (toScrollX < 0) {
toScrollX = 0;
} else if (toScrollX > menuWidth) {
toScrollX = menuWidth;
}
scrollTo(toScrollX, getScrollY());
startX = event.getX();
startY = event.getY();
float DX = Math.abs(endX - downX);
float DY = Math.abs(endY - downY);
if (DX > DY && DX > 8) {
//水平方向滑动
//相应侧滑
//反拦截-事件给slideLayout
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
int totalScrollX = getScrollX();
if (totalScrollX < menuWidth / 2) {
//关闭
closeMenu();
} else {
//打开
openMenu();
}
break;
}
return true;
}
7.内容视图设置点击事件时不能滑动item
内容添加点击事件
viewHolder.item_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyBean myBean1 = myBeans.get(position);
Toast.makeText(MainActivity.this, myBean1.getName(), Toast.LENGTH_SHORT).show();
}
});
//解决删除后还显示打开的删除 TextView
viewHolder.item_menu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SlideLayout slideLayout = (SlideLayout) v.getParent();
slideLayout.closeMenu();
myBeans.remove(myBean);
notifyDataSetChanged();
}
});
**分析原因事件被点击 TextView 事件消费解决方法,在 item 中拦截**
/**
* true:拦截孩子的事件,但会执行当前控件的onTouchEvent()方法
* false:不拦截孩子的事件,事件继续传递
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = startX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getX();
float endY = event.getY();
float distanceX=endX-startX;
startX=endX;
if(Math.abs((endX-downX))>8){
intercept=true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return intercept;
}
8.限制只能打开一个item
/**
* 状态改变接口
*/
interface OnStateChangeListenter{
void onClose(SlideLayout layout);
void onDown(SlideLayout layout);
void onOpen(SlideLayout layout);
}
OnStateChangeListenter onStateChangeListenter;
public void setOnStateChangeListenter(OnStateChangeListenter onStateChangeListenter) {
this.onStateChangeListenter = onStateChangeListenter;
}
**调用接口**
//打开菜单
public void openMenu() {
int distanceX = menuWidth - getScrollX();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
//强制刷新
invalidate();
if(onStateChangeListenter!=null){
onStateChangeListenter.onOpen(this);
}
}
//关闭菜单
public void closeMenu() {
int distanceX = 0 - getScrollX();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
//强制刷新
invalidate();
if(onStateChangeListenter!=null){
onStateChangeListenter.onClose(this);
}
}
**在 SlideLayout 中的 onInterceptTouchEvent 方法使用**
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = startX = event.getX();
if(onStateChangeListenter!=null){
onStateChangeListenter.onDown(this);
}
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getX();
float endY = event.getY();
float distanceX=endX-startX;
startX=endX;
if(Math.abs((endX-downX))>8){
intercept=true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return intercept;
}
在 MainActivity 的适配器 getView()方法中使用接口-设置点击事件
SlideLayout slideLayout = (SlideLayout) convertView;
slideLayout.setOnStateChangeListener(new MyOnStateChangeListener());
private SlideLayout slideLayout;
class MySlideLayout implements SlideLayout.OnStateChangeListenter {
@Override
public void onClose(SlideLayout layout) {//关闭的时候调用,将资源释放
if(slideLayout==layout){
slideLayout=null;
}
}
@Override
public void onDown(SlideLayout layout) {//只要触摸了一定会调用点击
if(slideLayout!=layout&&slideLayout!=null){
slideLayout.closeMenu();
}
}
@Override
public void onOpen(SlideLayout layout) {
slideLayout = layout;
}
}
上一篇: Cookie增删改查
下一篇: 刚才搜了一下,好像都挺复杂的。