Android自定義控件仿QQ抽屜效果
其實(shí)網(wǎng)上類似的實(shí)現(xiàn)已經(jīng)很多了,原理也并不難,只是網(wǎng)上各種demo運(yùn)行下來(lái),多少都有一些問(wèn)題。折騰了半天,決定自己實(shí)現(xiàn)一個(gè)。
首先我們看看實(shí)現(xiàn)效果:

對(duì)比網(wǎng)上各類demo,這次要實(shí)現(xiàn)的主要表現(xiàn)在以下幾點(diǎn):
1.側(cè)滑顯示抽屜view
2.側(cè)滑抽屜隱藏view控件點(diǎn)擊事件
3.單擊任意item隱藏顯示的抽屜view
4.滑動(dòng)list隱藏顯示的抽屜view
5.增加SwipeLayout點(diǎn)擊事件和Swipe touch事件判斷處理
6.優(yōu)化快速劃開(kāi)多個(gè)抽屜隱藏view時(shí)多個(gè)SwipeLayout滑動(dòng)狀態(tài)判斷處理,僅顯示最后一個(gè)滑動(dòng)的抽屜隱藏view,隱藏前面所有打開(kāi)的抽屜view(快速滑動(dòng)時(shí),可能存在多個(gè)抽屜view打開(kāi)情況,網(wǎng)上找的幾個(gè)demo主要問(wèn)題都集中在這一塊)
實(shí)現(xiàn)原理
其實(shí)單就一個(gè)SwipeLayout的實(shí)現(xiàn)原理來(lái)講的話,還是很簡(jiǎn)單的,實(shí)際上單個(gè)SwipeLayout隱藏抽屜狀態(tài)時(shí),應(yīng)該是這樣的:

也就是說(shuō),最初的隱藏狀態(tài),實(shí)際上是將hide view區(qū)域layout到conten view的右邊,達(dá)到隱藏效果,而后顯示則是根據(jù)拖拽的x值變化來(lái)動(dòng)態(tài)的layout 2個(gè)view,從而達(dá)到一個(gè)滑動(dòng)抽屜效果。
當(dāng)然,直接重寫(xiě)view的onTouchEvent來(lái)動(dòng)態(tài)的layout 2個(gè)view是可以實(shí)現(xiàn)我們需要的效果的,但是有更好的方法來(lái)實(shí)現(xiàn),就是同過(guò)ViewDragHelper。
ViewDragHelper是google官方提供的一個(gè)專門(mén)用于手勢(shì)分析處理的類,關(guān)于ViewDragHelper的基本使用,網(wǎng)上有一大堆的資源。具體的ViewDragHelper介紹以及基本使用方法,本文就不重復(fù)造輪子了,此處推薦鴻洋大神的一篇微博:Android ViewDragHelper完全解析 自定義ViewGroup神器。
具體實(shí)現(xiàn)
下面我們開(kāi)始具體的實(shí)現(xiàn)。
布局比較簡(jiǎn)單,這里就不貼代碼了,最后會(huì)貼上本demo的完整代碼地址。
首先我們實(shí)現(xiàn)一個(gè)繼承FrameLayout的自定義SwipeLauout,重寫(xiě)onFinishInflate方法:
這里我們只允許SwipeLayout設(shè)置2個(gè)子View,ContentLayout是繼承LinearLayout的自定義layout,后面會(huì)講到這個(gè),此處先略過(guò);
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 2) {
throw new IllegalStateException("Must 2 views in SwipeLayout");
}
contentView = getChildAt(0);
hideView = getChildAt(1);
if (contentView instanceof ContentLayout)
((ContentLayout) contentView).setSwipeLayout(this);
else {
throw new IllegalStateException("content view must be an instanceof FrontLayout");
}
}
接著重寫(xiě)onSizeChanged,onLayout,onInterceptTouchEvent方法:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
hideViewHeight = hideView.getMeasuredHeight();
hideViewWidth = hideView.getMeasuredWidth();
contentWidth = contentView.getMeasuredWidth();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// super.onLayout(changed, left, top, right, bottom);
contentView.layout(0, 0, contentWidth, hideViewHeight);
hideView.layout(contentView.getRight(), 0, contentView.getRight()
+ hideViewWidth, hideViewHeight);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
// Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");
return result;
}
然后是比較關(guān)鍵的,重寫(xiě)onTouchEvent方法以及ViewDragHelper.Callback回調(diào),我們定了一個(gè)enum來(lái)判斷SwipeLayout的三種狀態(tài)。在onViewPositionChanged中,有2種方法實(shí)現(xiàn)content view和hide view的伴隨移動(dòng),一種是直接offset view的橫向變化量,還有一種就是直接通過(guò)layout的方式,兩種方式都可以。
public enum SwipeState {
Open, Swiping, Close;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.e("SwipeLayout", "-----onTouchEvent-----");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
// 1.獲取x和y方向移動(dòng)的距離
float moveX = event.getX();
float moveY = event.getY();
float delatX = moveX - downX;// x方向移動(dòng)的距離
float delatY = moveY - downY;// y方向移動(dòng)的距離
if (Math.abs(delatX) > Math.abs(delatY)) {
// 表示移動(dòng)是偏向于水平方向,那么應(yīng)該SwipeLayout應(yīng)該處理,請(qǐng)求listview不要攔截
this.requestDisallowInterceptTouchEvent(true);
}
// 更新downX,downY
downX = moveX;
downY = moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
viewDragHelper.processTouchEvent(event);
return true;
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == contentView || child == hideView;
}
@Override
public int getViewHorizontalDragRange(View child) {
return hideViewWidth;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == contentView) {
if (left > 0)
left = 0;
if (left < -hideViewWidth)
left = -hideViewWidth;
} else if (child == hideView) {
if (left > contentWidth)
left = contentWidth;
if (left < (contentWidth - hideViewWidth))
left = contentWidth - hideViewWidth;
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == contentView) {
// 如果手指滑動(dòng)deleteView,那么也要講橫向變化量dx設(shè)置給contentView
hideView.offsetLeftAndRight(dx);
} else if (changedView == hideView) {
// 如果手指滑動(dòng)contentView,那么也要講橫向變化量dx設(shè)置給deleteView
contentView.offsetLeftAndRight(dx);
}
// if (changedView == contentView) {
// // 手動(dòng)移動(dòng)deleteView
// hideView.layout(hideView.getLeft() + dx,
// hideView.getTop() + dy, hideView.getRight() + dx,
// hideView.getBottom() + dy);
// } else if (hideView == changedView) {
// // 手動(dòng)移動(dòng)contentView
// contentView.layout(contentView.getLeft() + dx,
// contentView.getTop() + dy, contentView.getRight() + dx,
// contentView.getBottom() + dy);
// }
//實(shí)時(shí)更新當(dāng)前狀態(tài)
updateSwipeStates();
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//根據(jù)用戶滑動(dòng)速度處理開(kāi)關(guān)
//xvel: x方向滑動(dòng)速度
//yvel: y方向滑動(dòng)速度
// Log.e("tag", "currentState = " + currentState);
// Log.e("tag", "xvel = " + xvel);
if (xvel < -200 && currentState != SwipeState.Open) {
open();
return;
} else if (xvel > 200 && currentState != SwipeState.Close) {
close();
return;
}
if (contentView.getLeft() < -hideViewWidth / 2) {
// 打開(kāi)
open();
} else {
// 關(guān)閉
close();
}
}
};
open(),close()實(shí)現(xiàn)
public void open() {
open(true);
}
public void close() {
close(true);
}
/**
* 打開(kāi)的方法
*
* @param isSmooth 是否通過(guò)緩沖動(dòng)畫(huà)的形式設(shè)定view的位置
*/
public void open(boolean isSmooth) {
if (isSmooth) {
viewDragHelper.smoothSlideViewTo(contentView, -hideViewWidth,
contentView.getTop());
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
} else {
contentView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置
hideView.offsetLeftAndRight(-hideViewWidth);//直接偏移View的位置
// contentView.layout(-hideViewWidth, 0, contentWidth - hideViewWidth, hideViewHeight);//直接通過(guò)坐標(biāo)擺放
// hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通過(guò)坐標(biāo)擺放
invalidate();
}
}
/**
* 關(guān)閉的方法
*
* @param isSmooth true:通過(guò)緩沖動(dòng)畫(huà)的形式設(shè)定view的位置
* false:直接設(shè)定view的位置
*/
public void close(boolean isSmooth) {
if (isSmooth) {
viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());
ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
} else {
contentView.offsetLeftAndRight(hideViewWidth);
hideView.offsetLeftAndRight(hideViewWidth);
invalidate();
//contentView.layout(0, 0, contentWidth, hideViewHeight);//直接通過(guò)坐標(biāo)擺放
//hideView.layout(contentView.getRight(), 0, hideViewWidth, hideViewHeight);//直接通過(guò)坐標(biāo)擺放
}
}
此上基本實(shí)現(xiàn)了單個(gè)SwipeLayout的抽屜滑動(dòng)效果,但是將此SwipeLayout作為一個(gè)item布局設(shè)置給一個(gè)listView的時(shí)候,還需要做許多的判斷。
由于listView的重用機(jī)制,我們這里并未針對(duì)listview做任何處理,所以一旦有一個(gè)item的SwipeLayout的狀態(tài)是打開(kāi)狀態(tài),不可避免的其它也必然有幾個(gè)是打開(kāi)狀態(tài),所以我們這里需要根據(jù)檢測(cè)listView的滑動(dòng),當(dāng)listView滑動(dòng)時(shí),關(guān)閉SwipeLayout。既然需要在外部控制SwipeLayout的開(kāi)關(guān),我們先定義一個(gè)SwipeLayoutManager用于管理SwipeLayout的控制。
public class SwipeLayoutManager {
//記錄打開(kāi)的SwipeLayout集合
private HashSet<SwipeLayout> mUnClosedSwipeLayouts = new HashSet<SwipeLayout>();
private SwipeLayoutManager() {
}
private static SwipeLayoutManager mInstance = new SwipeLayoutManager();
public static SwipeLayoutManager getInstance() {
return mInstance;
}
/**
* 將一個(gè)沒(méi)有關(guān)閉的SwipeLayout加入集合
* @param layout
*/
public void add(SwipeLayout layout) {
mUnClosedSwipeLayouts.add(layout);
}
/**
* 將一個(gè)沒(méi)有關(guān)閉的SwipeLayout移出集合
* @param layout
*/
public void remove(SwipeLayout layout){
mUnClosedSwipeLayouts.remove(layout);
}
/**
* 關(guān)閉已經(jīng)打開(kāi)的SwipeLayout
*/
public void closeUnCloseSwipeLayout() {
if(mUnClosedSwipeLayouts.size() == 0){
return;
}
for(SwipeLayout l : mUnClosedSwipeLayouts){
l.close(true);
}
mUnClosedSwipeLayouts.clear();
}
/**
* 關(guān)閉已經(jīng)打開(kāi)的SwipeLayout
*/
public void closeUnCloseSwipeLayout(boolean isSmooth) {
if(mUnClosedSwipeLayouts.size() == 0){
return;
}
for(SwipeLayout l : mUnClosedSwipeLayouts){
l.close(isSmooth);
}
mUnClosedSwipeLayouts.clear();
}
}
這樣就可以監(jiān)聽(tīng)listView的滑動(dòng),然后在listView滑動(dòng)的時(shí)候,關(guān)閉所有的抽屜View。
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
swipeLayoutManager.closeUnCloseSwipeLayout();
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
考慮到大多數(shù)時(shí)候,在我們打開(kāi)抽屜View和關(guān)閉抽屜View的時(shí)候,外部需要知道SwipeLayout的狀態(tài)值,所以我們需要在SwipeLayout中增加幾個(gè)接口,告訴外部當(dāng)前SwipeLayout的狀態(tài)值:
SwipeLayout.java
----------------
private void updateSwipeStates() {
SwipeState lastSwipeState = currentState;
SwipeState swipeState = getCurrentState();
if (listener == null) {
try {
throw new Exception("please setOnSwipeStateChangeListener first!");
} catch (Exception e) {
e.printStackTrace();
}
return;
}
if (swipeState != currentState) {
currentState = swipeState;
if (currentState == SwipeState.Open) {
listener.onOpen(this);
// 當(dāng)前的Swipelayout已經(jīng)打開(kāi),需要讓Manager記錄
swipeLayoutManager.add(this);
} else if (currentState == SwipeState.Close) {
listener.onClose(this);
// 說(shuō)明當(dāng)前的SwipeLayout已經(jīng)關(guān)閉,需要讓Manager移除
swipeLayoutManager.remove(this);
} else if (currentState == SwipeState.Swiping) {
if (lastSwipeState == SwipeState.Open) {
listener.onStartClose(this);
} else if (lastSwipeState == SwipeState.Close) {
listener.onStartOpen(this);
//hideView準(zhǔn)備顯示之前,先將之前打開(kāi)的的SwipeLayout全部關(guān)閉
swipeLayoutManager.closeUnCloseSwipeLayout();
swipeLayoutManager.add(this);
}
}
} else {
currentState = swipeState;
}
}
/**
* 獲取當(dāng)前控件狀態(tài)
*
* @return
*/
public SwipeState getCurrentState() {
int left = contentView.getLeft();
// Log.e("tag", "contentView.getLeft() = " + left);
// Log.e("tag", "hideViewWidth = " + hideViewWidth);
if (left == 0) {
return SwipeState.Close;
}
if (left == -hideViewWidth) {
return SwipeState.Open;
}
return SwipeState.Swiping;
}
private OnSwipeStateChangeListener listener;
public void setOnSwipeStateChangeListener(
OnSwipeStateChangeListener listener) {
this.listener = listener;
}
public View getContentView() {
return contentView;
}
public interface OnSwipeStateChangeListener {
void onOpen(SwipeLayout swipeLayout);
void onClose(SwipeLayout swipeLayout);
void onStartOpen(SwipeLayout swipeLayout);
void onStartClose(SwipeLayout swipeLayout);
}
然后接下來(lái)是寫(xiě)一個(gè)為listView設(shè)置的SwipeAdapter
SwipeAdapter.java
------------
public class SwipeAdapter extends BaseAdapter implements OnSwipeStateChangeListener {
private Context mContext;
private List<String> list;
private MyClickListener myClickListener;
private SwipeLayoutManager swipeLayoutManager;
public SwipeAdapter(Context mContext) {
super();
this.mContext = mContext;
init();
}
private void init() {
myClickListener = new MyClickListener();
swipeLayoutManager = SwipeLayoutManager.getInstance();
}
public void setList(List<String> list){
this.list = list;
notifyDataSetChanged();
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = UIUtils.inflate(R.layout.list_item_swipe);
}
ViewHolder holder = ViewHolder.getHolder(convertView);
holder.tv_content.setText(list.get(position));
holder.tv_overhead.setOnClickListener(myClickListener);
holder.tv_overhead.setTag(position);
holder.tv_delete.setOnClickListener(myClickListener);
holder.tv_delete.setTag(position);
holder.sv_layout.setOnSwipeStateChangeListener(this);
holder.sv_layout.setTag(position);
holder.sv_layout.getContentView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.showToast("item click : " + position);
swipeLayoutManager.closeUnCloseSwipeLayout();
}
});
return convertView;
}
static class ViewHolder {
TextView tv_content, tv_overhead, tv_delete;
SwipeLayout sv_layout;
public ViewHolder(View convertView) {
tv_content = (TextView) convertView.findViewById(R.id.tv_content);
tv_overhead = (TextView) convertView.findViewById(R.id.tv_overhead);
tv_delete = (TextView) convertView.findViewById(R.id.tv_delete);
sv_layout = (SwipeLayout) convertView.findViewById(R.id.sv_layout);
}
public static ViewHolder getHolder(View convertView) {
ViewHolder holder = (ViewHolder) convertView.getTag();
if (holder == null) {
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
return holder;
}
}
class MyClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Integer position = (Integer) v.getTag();
switch (v.getId()) {
case R.id.tv_overhead:
//ToastUtils.showToast("position : " + position + " overhead is clicked.");
}
break;
case R.id.tv_delete:
//ToastUtils.showToast("position : " + position + " delete is clicked.");
}
break;
default:
break;
}
}
}
@Override
public void onOpen(SwipeLayout swipeLayout) {
//ToastUtils.showToast(swipeLayout.getTag() + "onOpen.");
}
@Override
public void onClose(SwipeLayout swipeLayout) {
//ToastUtils.showToast(swipeLayout.getTag() + "onClose.");
}
@Override
public void onStartOpen(SwipeLayout swipeLayout) {
// ToastUtils.showToast("onStartOpen.");
}
@Override
public void onStartClose(SwipeLayout swipeLayout) {
// ToastUtils.showToast("onStartClose.");
}
}
此時(shí)已經(jīng)基本實(shí)現(xiàn)了我們需要的大部分功能了,但是當(dāng)我們滑動(dòng)的時(shí)候,又發(fā)現(xiàn)新的問(wèn)題,我們的SwipeLayout和listview滑動(dòng)判斷有問(wèn)題。由于前面我們僅僅是將touch攔截事件簡(jiǎn)簡(jiǎn)單單的丟給了viewDragHelper.shouldInterceptTouchEvent(ev)來(lái)處理,導(dǎo)致SwipeLayout和listview攔截touch事件時(shí)的處理存在一定的問(wèn)題,這里我們要提到一個(gè)知識(shí)點(diǎn):Android view事件的傳遞。
(1)首先由Activity分發(fā),分發(fā)給根View,也就是DecorView(DecorView為整個(gè)Window界面的最頂層View)
(2)然后由根View分發(fā)到子的View
view事件攔截如下圖所示:

view事件的消費(fèi)如下圖所示:

注:以上2張圖借鑒網(wǎng)上總結(jié)的比較經(jīng)典的圖
所以這里我們就要談到一開(kāi)始出現(xiàn)的ContentLayout,主要重寫(xiě)了onInterceptTouchEvent和onTouchEvent。
public class ContentLayout extends LinearLayout {
SwipeLayoutInterface mISwipeLayout;
public ContentLayout(Context context) {
super(context);
}
public ContentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ContentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSwipeLayout(SwipeLayoutInterface iSwipeLayout) {
this.mISwipeLayout = iSwipeLayout;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Log.e("ContentLayout", "-----onInterceptTouchEvent-----");
if (mISwipeLayout.getCurrentState() == SwipeState.Close) {
return super.onInterceptTouchEvent(ev);
} else {
return true;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Log.e("ContentLayout", "-----onTouchEvent-----");
if (mISwipeLayout.getCurrentState() == SwipeState.Close) {
return super.onTouchEvent(ev);
} else {
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mISwipeLayout.close();
}
return true;
}
}
}
另外由于在ContentLayout中需要拿到父View SwipeLayout的開(kāi)關(guān)狀態(tài)以及控制SwipeLayout的關(guān)閉,因此在再寫(xiě)一個(gè)接口,用于ContentLayout獲取SwipeLayout的開(kāi)關(guān)狀態(tài)以及更新SwipeLayout。
public interface SwipeLayoutInterface {
SwipeState getCurrentState();
void open();
void close();
}
然后接著的是完善SwipeLayout的onInterceptTouchEvent,我們?cè)谶@里增加一個(gè)GestureDetectorCompat處理手勢(shì)識(shí)別:
private void init(Context context) {
viewDragHelper = ViewDragHelper.create(this, callback);
mGestureDetector = new GestureDetectorCompat(context, mOnGestureListener);
swipeLayoutManager = SwipeLayoutManager.getInstance();
}
private SimpleOnGestureListener mOnGestureListener = new SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// 當(dāng)橫向移動(dòng)距離大于等于縱向時(shí),返回true
return Math.abs(distanceX) >= Math.abs(distanceY);
}
};
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev) & mGestureDetector.onTouchEvent(ev);
// Log.e("SwipeLayout", "-----onInterceptTouchEvent-----");
return result;
}
如此下來(lái),整個(gè)View不管是上下拖動(dòng),還是SwipeLayout的開(kāi)關(guān)滑動(dòng),都已經(jīng)實(shí)現(xiàn)完成了。最后增加對(duì)應(yīng)overhead,delete以及item的點(diǎn)擊事件,此處完善SwipeAdapter的代碼之后如下。
class MyClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Integer position = (Integer) v.getTag();
switch (v.getId()) {
case R.id.tv_overhead:
//ToastUtils.showToast("position : " + position + " overhead is clicked.");
swipeLayoutManager.closeUnCloseSwipeLayout(false);
if(onSwipeControlListener != null){
onSwipeControlListener.onOverhead(position, list.get(position));
}
break;
case R.id.tv_delete:
//ToastUtils.showToast("position : " + position + " delete is clicked.");
swipeLayoutManager.closeUnCloseSwipeLayout(false);
if(onSwipeControlListener != null){
onSwipeControlListener.onDelete(position, list.get(position));
}
break;
default:
break;
}
}
}
private OnSwipeControlListener onSwipeControlListener;
public void setOnSwipeControlListener(OnSwipeControlListener onSwipeControlListener){
this.onSwipeControlListener = onSwipeControlListener;
}
/**
* overhead 和 delete點(diǎn)擊事件接口
*/
public interface OnSwipeControlListener{
void onOverhead(int position, String itemTitle);
void onDelete(int position, String itemTitle);
}
最后貼上MainActivity代碼,此處通過(guò)OnSwipeControlListener接口回調(diào)實(shí)現(xiàn)item的刪除和置頂:
public class MainActivity extends Activity implements OnSwipeControlListener {
private ListView listView;
private List<String> list = new ArrayList<String>();
private SwipeLayoutManager swipeLayoutManager;
private SwipeAdapter swipeAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initData() {
for (int i = 0; i < 50; i++) {
list.add("content - " + i);
}
}
private void initView() {
swipeLayoutManager = SwipeLayoutManager.getInstance();
swipeAdapter = new SwipeAdapter(this);
swipeAdapter.setList(list);
listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(swipeAdapter);
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
swipeLayoutManager.closeUnCloseSwipeLayout();
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
swipeAdapter.setOnSwipeControlListener(this);
}
@Override
public void onOverhead(int position, String itemTitle) {
setItemOverhead(position, itemTitle);
}
@Override
public void onDelete(int position, String itemTitle) {
removeItem(position, itemTitle);
}
/**
* 設(shè)置item置頂
*
* @param position
* @param itemTitle
*/
private void setItemOverhead(int position, String itemTitle) {
// ToastUtils.showToast("position : " + position + " overhead.");
ToastUtils.showToast("overhead ---" + itemTitle + "--- success.");
String newTitle = itemTitle;
list.remove(position);//刪除要置頂?shù)膇tem
list.add(0, newTitle);//根據(jù)adapter傳來(lái)的Title數(shù)據(jù)在list 0位置插入title字符串,達(dá)到置頂效果
swipeAdapter.setList(list);//重新給Adapter設(shè)置list數(shù)據(jù)并更新
UIUtils.runOnUIThread(new Runnable() {
@Override
public void run() {
listView.setSelection(0);//listview選中第0項(xiàng)item
}
});
}
/**
* 刪除item
*
* @param position
* @param itemTitle
*/
private void removeItem(int position, String itemTitle) {
// ToastUtils.showToast("position : " + position + " delete.");
ToastUtils.showToast("delete ---" + itemTitle + "--- success.");
list.remove(position);
swipeAdapter.setList(list);//重新給Adapter設(shè)置list數(shù)據(jù)并更新
}
}
至此整個(gè)demo基本完成,本次完成的功能基本能夠直接放到項(xiàng)目中使用。其實(shí)最麻煩的地方就在于view的touch事件攔截和處理,不過(guò)將本demo的log打開(kāi)看一下對(duì)比之后,也就能夠理解整個(gè)傳遞過(guò)程了。
完整demo地址:https://github.com/Horrarndoo/SwipeLayout
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android開(kāi)發(fā)之DrawerLayout實(shí)現(xiàn)抽屜效果
- Android編程實(shí)現(xiàn)抽屜效果的方法詳解
- Android 自定義View實(shí)現(xiàn)抽屜效果
- Android DrawerLayout實(shí)現(xiàn)抽屜效果實(shí)例代碼
- Android 抽屜效果的導(dǎo)航菜單實(shí)現(xiàn)代碼實(shí)例
- Android實(shí)現(xiàn)自定義滑動(dòng)式抽屜菜單效果
- Android App中DrawerLayout抽屜效果的菜單編寫(xiě)實(shí)例
- Android SlidingDrawer 抽屜效果的實(shí)現(xiàn)
- Android的Activity跳轉(zhuǎn)動(dòng)畫(huà)各種效果整理
- Android Tween動(dòng)畫(huà)之RotateAnimation實(shí)現(xiàn)圖片不停旋轉(zhuǎn)效果實(shí)例介紹
- Android實(shí)現(xiàn)圖片輪播效果的兩種方法
- Android編程實(shí)現(xiàn)抽屜效果的方法示例
相關(guān)文章
Android?OkHttp庫(kù)簡(jiǎn)單使用和封裝教程助你快速掌握網(wǎng)絡(luò)請(qǐng)求技能
OkHttp是一個(gè)高效的HTTP客戶端庫(kù),適用于Android和Java應(yīng)用程序。它支持HTTP/2和SPDY協(xié)議,提供了同步和異步請(qǐng)求API、請(qǐng)求和響應(yīng)攔截器、連接池和多路復(fù)用器、緩存支持、GZIP和DEFLATE壓縮等功能,可以大大提高網(wǎng)絡(luò)請(qǐng)求的性能和可擴(kuò)展性2023-04-04
Android 藍(lán)牙開(kāi)發(fā)實(shí)例解析
本文主要介紹Android 藍(lán)牙開(kāi)發(fā),這里提供實(shí)例代碼和詳細(xì)解析實(shí)現(xiàn)方法,對(duì)開(kāi)發(fā)Android藍(lán)牙開(kāi)發(fā)的朋友提供簡(jiǎn)單示例,有需要的朋友可以參考下2016-08-08
詳解Android App中的AsyncTask異步任務(wù)執(zhí)行方式
這篇文章主要介紹了Android App中的AsyncTask異步任務(wù)執(zhí)行方式,文中舉了一個(gè)打開(kāi)網(wǎng)絡(luò)圖片的例子幫助大家直觀理解,需要的朋友可以參考下2016-04-04
Android 中ScrollView與ListView沖突問(wèn)題的解決辦法
這篇文章主要介紹了Android 中ScrollView與ListView沖突問(wèn)題的解決辦法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家掌握解決問(wèn)題的辦法,需要的朋友可以參考下2017-10-10
Adapter實(shí)現(xiàn)ListView帶多選框等狀態(tài)的自定義控件的注意事項(xiàng)
Android本身為L(zhǎng)istView提供了幾個(gè)方便的Adapter,比如ArrayAdapter、SimpleCurrentAdapter等等,接下來(lái)介紹自定義Adapter實(shí)現(xiàn)ListView帶多選框等狀態(tài)控件的注意事項(xiàng),感興趣的朋友可以詳細(xì)了解下,或許對(duì)你有所幫助2013-01-01
Android 用Time和Calendar獲取系統(tǒng)當(dāng)前時(shí)間源碼分享(年月日時(shí)分秒周幾)
這篇文章主要介紹了Android 用Time和Calendar獲取系統(tǒng)當(dāng)前時(shí)間源碼分享,包括年月日時(shí)分秒周幾的源碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01
Android使用ShareSDK實(shí)現(xiàn)應(yīng)用分享的功能
這篇文章主要為大家詳細(xì)介紹了Android使用ShareSDK實(shí)現(xiàn)應(yīng)用分享的功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android Flutter實(shí)現(xiàn)五種酷炫文字動(dòng)畫(huà)效果詳解
animated_text_kit這一動(dòng)畫(huà)庫(kù)有多種文字動(dòng)畫(huà)效果,文中將利用它實(shí)現(xiàn)五種酷炫的文字動(dòng)畫(huà):波浪涌動(dòng)效果、波浪線跳動(dòng)文字組、彩虹動(dòng)效、滾動(dòng)廣告牌效果和打字效果,需要的可以參考一下2022-03-03
Android編程實(shí)現(xiàn)自定義控件的方法示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)自定義控件的方法,結(jié)合實(shí)例形式分析了Android自定義控件的布局、功能實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下2017-06-06

