仿ios簡訊列表滑動出現刪除按鈕
最近還沒找到工作所以在宿舍有點閒,所以呢就自己寫了這麼一個例子。之前網上有很多人寫過類似的文章或demo,github上面也有開源專案。但是,老是copy別人程式碼也怪沒意思的,於是就自己嘗試著自己寫唄。廢話多了先來看下效果咯:
執行效果:
1、實現思路
其實實現思路很簡單那就是在ListView的item裡面放一個可以滑動的view,這裡我用的是HorizontalScrollView。我之所以選HorizontalScrollView是因為可以不用去處理move事件
2、SildingView的實現
SildingView是我自定義的一個View 他繼承的是HorizontalScrollView,SildingView就是我們要放到ListView item裡面的View。程式碼如下所示:
SlideView.java
package okhttp.lc.com.lateralslidingitem;
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.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
* Created by HP on 2016/6/6.
*/
public class SildingView extends HorizontalScrollView {
int mScreenWidth;
boolean once = false;
//將MenuItem的寬度設定為100px
int MenuWidth = 100;
LinearLayout wapperView;
LinearLayout mContent;
LinearLayout mMenu;
OpenStatusListener mListener;
//判斷選單是否開啟
boolean isOpen = false;
public SildingView(Context context) {
this(context, null);
}
public SildingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SildingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void setContent(View view) {
addView(view);
}
private void init(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
}
/**
* 設定ItemMenu開啟監聽事件
* @param mListener
*/
public void setOpenStatusListener(OpenStatusListener mListener) {
this.mListener = mListener;
}
public interface OpenStatusListener {
//status 0表示開啟 1表示關閉
void getOpenStatus(int status);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP: {
if (getScrollX() >= MenuWidth / 2) {
smoothScrollTo(MenuWidth, 0);
isOpen = true;
mListener.getOpenStatus(0);
} else {
smoothScrollTo(0, 0);
isOpen = false;
mListener.getOpenStatus(1);
}
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 開啟ItemMenu
*/
public void openItemMenu() {
if (!isOpen) {
smoothScrollTo(MenuWidth, 0);
isOpen = true;
mListener.getOpenStatus(0);
}
}
public boolean getStatus() {
return isOpen;
}
/**
* 關閉ItemMenu
*/
public void closeItemMenu() {
if (isOpen) {
smoothScrollTo(0, 0);
isOpen = false;
mListener.getOpenStatus(1);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//onMeasure會多次呼叫,用once來控制以防多次執行以下程式碼
if (!once) {
wapperView = (LinearLayout) getChildAt(0);
mContent = (LinearLayout) wapperView.getChildAt(0);
mMenu = (LinearLayout) wapperView.getChildAt(1);
mContent.getLayoutParams().width = mScreenWidth;
mMenu.getLayoutParams().width = MenuWidth;
once = true;
}
}
}
3、Activity的實現
package okhttp.lc.com.lateralslidingitem;
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ListView lv_main;
List<String> list = new ArrayList<String>();
okhttp.lc.com.lateralslidingitem.SildingView mSlidingView;
MyAdapter adapter;
boolean isOpen = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
}
private void initData() {
for (int i = 0; i < 40; i++) {
list.add("SomeThing" + (i + 1));
}
adapter = new MyAdapter(this);
lv_main.setAdapter(adapter);
/**
* 監聽ListView的滑動
*/
lv_main.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
int action = motionEvent.getAction();
if (check()) {
Log.e("TAG", "true");
switch (action) {
case MotionEvent.ACTION_DOWN: {
close();
}
break;
case MotionEvent.ACTION_MOVE: {
close();
}
break;
case MotionEvent.ACTION_UP: {
close();
}
break;
}
return true;
}
return false;
}
});
}
/**
* 關閉ItemMenu
*/
private void close() {
Log.e("TAG", "close");
for (int i = 0; i < lv_main.getChildCount(); i++) {
SildingView v = (SildingView) lv_main.getChildAt(i);
v.closeItemMenu();
}
}
/**
* 檢查是否有開啟的ItemMenu
* @return
*/
private boolean check() {
for (int i = 0; i < lv_main.getChildCount(); i++) {
SildingView v2 = (SildingView) lv_main.getChildAt(i);
if (v2.getStatus()) {
isOpen = true;
return isOpen;
}
}
return false;
}
private void initViews() {
lv_main = (ListView) findViewById(R.id.lv_main);
}
class MyAdapter extends BaseAdapter implements SildingView.OpenStatusListener {
private LayoutInflater mInflater;
private Context mContext;
MyAdapter(Context mContext) {
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder mViewHolder = null;
mSlidingView = (SildingView) view;
if (mSlidingView == null) {
mViewHolder = new ViewHolder();
View itemview = mInflater.inflate(R.layout.item, null);
mSlidingView = new SildingView(MainActivity.this);
/**
* 貌似HorizontalScrollView的click事件被遮蔽了,應此為了實現點選效果採用了這種方法。
*/
final LinearLayout ll_item = (LinearLayout) itemview.findViewById(R.id.ll_item);
final int in=i;
ll_item.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (check()) {
close();
} else
Toast.makeText(getApplicationContext(), "click" + in, Toast.LENGTH_SHORT).show();
}
});
mSlidingView.setContent(itemview);
mSlidingView.setOpenStatusListener(this);
mViewHolder.textView = (TextView) mSlidingView.findViewById(R.id.textView);
mViewHolder.delete = (TextView) mSlidingView.findViewById(R.id.delete);
mSlidingView.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) mSlidingView.getTag();
}
mViewHolder.textView.setText(getItem(i).toString());
mViewHolder.delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "delete", Toast.LENGTH_LONG).show();
}
});
return mSlidingView;
}
@Override
public void getOpenStatus(int status) {
if (status == 0) {
Toast.makeText(getApplicationContext(), "開啟", Toast.LENGTH_SHORT).show();
// isOpen = true;
} else {
Toast.makeText(getApplicationContext(), "關閉" , Toast.LENGTH_SHORT).show();
// isOpen = false;
}
}
}
class ViewHolder {
TextView textView;
TextView delete;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, Activity2.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
同樣也是將MainActivity的程式碼全部貼出來,為了方便我寫了一個靜態內部的Adapter。在getView()方法中將第三個引數強制轉化為SildingView,然後定義一個item_view來獲取載入好的佈局檔案,再將item_view做為SildingView的內容新增進去,最後返回SildingView。這樣就很好的實現了將SildingView做為ListView的item新增進去,呃描述的實在不好能力有限啊還是看程式碼理解吧。。。。
如果仔細一點會發現iphone手機簡訊列表,如果有一個item的ItemMenu處於開啟狀態那麼接下來無論使用者是滑動列表還是點選列表的item都將沒有應有的效果,而產生的效果就是將開啟的ItemMenu關閉。為了實現這個效果我重寫了ListView的onTouch()方法,在move事件中通過遍歷ListView的所有item來檢查是否有item的ItemMenu已經開啟如果有則將其關閉並且遮蔽掉onTouchEvent()方法使得ListView無法滑動。最後一個問題就是item的點選事件的問題,因為我們將View換成了SlidingView(SlidingView是一個HorizontalScrollView,而HorizontalScrollView貌似將click事件遮蔽掉了)導致我們給ListView設定onItemClick事件失效,因此我選擇在getView()方法中設定SlidingView中的子View的click事件這樣同樣實現了ListView的調集效果。但是這絕對不是最好的方法
4、佈局檔案
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
item.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"
android:descendantFocusability="blocksDescendants"
android:id="@+id/ll_item"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#50000000"
>
<TextView
android:id="@+id/delete"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="delete"
android:gravity="center"
/>
</LinearLayout>
</LinearLayout>