此博客利用ViewDragHelper实现qq消息栏侧滑删除效果,同时这也是中国大学慕课移动终端应用开发的网课作业9,我会持续更新我的作业,如果有需要关注一下吧
说明
1.由于涉及到没有学过的内容,自学的时候参考了此篇博文和菜鸟教程,并在原基础上做了优化,并补充了几乎每个新知识的注释。
2.粘贴的时候注意包的正确导入,Message实体类不要和安卓的消息类混淆
3.注意修改布局文件item.xml上ViewGroup的包名,android studio不会主动报错,但是要修改,不然无法通过编译
4.还有其他注意点我想到了再补充……嘿嘿
哦对了,作业8太简单我就不写了
效果图
代码部分
代码分成四个部分,第一部分是消息实体类Message.java,第二部分是ListView的子布局,涉及到item.xml布局文件和TranslationLayout.java自定义布局,第三部分是适配器MessageAdapter.java,第四部分是主界面MainActivity.java和activity_main.xml主布局文件。以下是具体内容:
Message.java
/**
* 消息实体类
* */
public class Message {
private String name; //姓名
private String content; //内容
private String date; //日期
private int img; //头像id资源
public Message() {
}
public Message(String name, String content) {
this.name = name;
this.content = content;
this.date = "6:00 am";
this.img = R.drawable.boy;
}
public Message(String name, String content, String date, int img) {
this.name = name;
this.content = content;
this.date = date;
this.img = img;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getImg() {
return img;
}
public void setImg(int img) {
this.img = img;
}
}
item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.course9.mylayout.TranslationLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="5dp"
android:background="#DCF5F1">
<ImageView
android:id="@+id/image"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/boy"
/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/image"
android:layout_alignBottom="@+id/image"
android:layout_toRightOf="@+id/image"
android:layout_marginLeft="15dp"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="大青儿"
android:textSize="25dp"
android:textColor="#F1A46C"
/>
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="2dp"
android:text="早上好铁子"
android:textSize="15dp"
android:textColor="#222121"
android:layout_alignParentBottom="true"
/>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="6:00 am"
android:textSize="19sp"
android:textColor="#FF7F7F80"
android:layout_alignParentRight="true"
android:padding="10dp"
android:paddingRight="20dp"
/>
</RelativeLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="200dp"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:id="@+id/to_top"
android:layout_width="66.5dp"
android:layout_height="match_parent"
android:text="置顶"
android:textColor="#FFF"
android:textSize="17sp"
android:gravity="center"
android:background="#FFC6C6CC"/>
<TextView
android:id="@+id/have_read"
android:layout_width="66.5dp"
android:layout_height="match_parent"
android:textColor="#FFF"
android:textSize="17sp"
android:text="已读"
android:gravity="center"
android:background="#FFFD9C01"/>
<TextView
android:id="@+id/delete"
android:layout_width="66.5dp"
android:layout_height="match_parent"
android:textColor="#FFF"
android:textSize="17sp"
android:text="删除"
android:gravity="center"
android:background="#FFFD3A30" />
</LinearLayout>
</com.example.course9.mylayout.TranslationLayout>
TranslationLayout.java
此为自定义布局,这一部分是本博客的重中之重,几乎每一处的代码我都写有注释,李姐万岁
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;
public class TranslationLayout extends LinearLayout {
private ViewDragHelper mViewDragHelper;//定义mViewDragHelper
public boolean directionFlag = true;//移动方向的标志,true为左,false为右
private int screenWidth;//屏幕的宽度
private Context mContext;//上下文对象
private View firstView;//第一个view对象
private View secondView;//第二个view对象
private int secondViewWidth;//第二个view的长度
private final String TAG = "TranslationLayoutTAG";//logcat的标记
public TranslationLayout(Context context) {
super(context);
this.mContext = context;
init();
}
public TranslationLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
init();
}
public TranslationLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
init();
}
//初始化方法
private void init(){
mViewDragHelper = ViewDragHelper.create(this,new MyCallBack());
//获取屏幕的长度
WindowManager wm=(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
screenWidth = outMetrics.widthPixels;
}
//在第一次渲染结束的时候,获取两个子View
@Override
protected void onFinishInflate() {
super.onFinishInflate();
firstView = getChildAt(0);//获取第一个子view,一开始显示的那个
secondView = getChildAt(1);//获取第二个view,后面隐藏的,侧滑之后才能看见
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取第二个view的长度
secondViewWidth = secondView.getMeasuredWidth();
}
//在onInterceptTouchEvent中授权mViewDragHelper.shouldInterceptTouchEvent(ev)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
//在onTouchEvent中授权 mViewDragHelper.processTouchEvent(event);
@Override
public boolean onTouchEvent(MotionEvent event) {
// 处理相应的TouchEvent的时候要将结果返回为true,消费本次事件
//否则将无法使用ViewDragHelper处理相应的拖拽事件
mViewDragHelper.processTouchEvent(event);
return true;
}
//重写的ViewGroup的方法,主要是用于ViewGroup中更新相应View
@Override
public void computeScroll() {
super.computeScroll();
if(mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
//内部类
private class MyCallBack extends ViewDragHelper.Callback{
private int left;
//必须实现此方法,也只有在这个方法返回true的时候下面的方法才会生效
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
return true;
}
/**
* 当状态改变的时候回调,返回相应的状态(这里有三种状态)
* STATE_IDLE 闲置状态
* STATE_DRAGGING 正在拖动
* STATE_SETTLING 放置到某个位置
* */
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
/**
* 位置发生改变的时候回调
* 参数1:你当前拖动的这个View
* 参数2:距离左边的距离
* 参数3:距离上边的距离
* 参数4:x轴的变化量
* 参数5:y轴的变化量
* */
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
//如果第一个view被拖动,第二个view(被隐藏那个)也跟着移动
if (changedView == firstView){
secondView.offsetLeftAndRight(dx);
}else {//如果移动的是第二个,第一个view也跟着移动
firstView.offsetLeftAndRight(dx);
}
this.left = left;
//更新ui
invalidate();
}
/**
* 在该方法中对child移动的水平边界进行控制,left表示即将移动到的位置。
* */
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
return Math.min(Math.max(-secondViewWidth, left), 0);//使其不能向右滑动;
}
/**
* 垂直边界进行控制
* */
@Override
public int getViewVerticalDragRange(View child) {
return 0;
}
//停止拖拽的时候
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
Log.d(TAG,"firstView.getRight():"+firstView.getRight());
//getRight()方法返回的是组件右边到屏幕左边的距离
//触发左移的条件是移动超过50dp并且方向向左
if(firstView.getRight()<(screenWidth-50)&&directionFlag){
//将第一个View向左移动第二个view宽度
mViewDragHelper.smoothSlideViewTo(firstView,-secondViewWidth, 0);
//更新ui
ViewCompat.postInvalidateOnAnimation(TranslationLayout.this);
//方向设置为右边
directionFlag = false;
} else {//方向为右边或者移动小于50dp
//将第一个View移动回去
mViewDragHelper.smoothSlideViewTo(firstView, 0, 0);
//更新ui
ViewCompat.postInvalidateOnAnimation(TranslationLayout.this);
//方向设置为左边
directionFlag = true;
}
}
}
}
activity_main.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">
<ListView
android:id="@+id/my_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
</ListView>
</LinearLayout>
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import com.example.course9.adapter.MessageAdapter;
import com.example.course9.model.Message;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ListView mListView;//定义ListView
private ArrayList<Message> mMessages;
private void init(){
mListView = findViewById(R.id.my_list_view);
mMessages = new ArrayList<>();
//放入数据
Message message1 = new Message("大青儿","今天早上我这好像下雪了,你那边呢");
Message message2 = new Message("小钱","和平精英,速速上线,快快快!!!");
Message message3 = new Message("小谢","科目三好难啊,感觉比考研还难咋办啊铁子");
Message message4 = new Message("小王","明天出来搓一顿啊");
Message message5 = new Message("特朗普","我比任何人都要懂安卓,我知道");
Message message6 = new Message("老妈","今天天气好冷,你要多穿一点衣服");
mMessages.add(message1);
mMessages.add(message2);
mMessages.add(message3);
mMessages.add(message4);
mMessages.add(message5);
mMessages.add(message6);
MessageAdapter adapter = new MessageAdapter(this,mMessages);
mListView.setAdapter(adapter);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
}
图片资源
图片资源来自阿里巴巴矢量图标库
boy.png,放入drawable文件夹中
最后
码字不易,如有帮助,点赞关注分享给个好评哦亲