此博客利用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;
}
}
}
}
MessageAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.course9.R;
import com.example.course9.model.Message;
import java.util.ArrayList;
public class MessageAdapter extends BaseAdapter {
private Context mContext;//上下文對象
private ArrayList<Message> mMessages;//對象數組
//有參構造方法
public MessageAdapter(Context context, ArrayList<Message> messages) {
mContext = context;
mMessages = messages;
}
//無參構造方法
public MessageAdapter() { }
//返回數據大小
@Override
public int getCount() {
return mMessages.size();
}
@Override
public Object getItem(int position) {
return mMessages.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null){
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item,parent,false);
holder.mImageViewImg = convertView.findViewById(R.id.image);
holder.mTextViewName = convertView.findViewById(R.id.name);
holder.mTextViewContent = convertView.findViewById(R.id.content);
holder.mTextViewDate = convertView.findViewById(R.id.time);
convertView.setTag(holder); //將Holder存儲到convertView中
}else {
holder = (ViewHolder) convertView.getTag();
}
holder.mImageViewImg.setBackgroundResource(mMessages.get(position).getImg());
holder.mTextViewName.setText(mMessages.get(position).getName());
holder.mTextViewContent.setText(mMessages.get(position).getContent());
holder.mTextViewDate.setText(mMessages.get(position).getDate());
return convertView;
}
//內部類
private class ViewHolder{
public ImageView mImageViewImg;
public TextView mTextViewName,mTextViewContent,mTextViewDate;
}
}
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文件夾中
最後
碼字不易,如有幫助,點贊關注分享給個好評哦親