編輯:關於Android編程
慣例,先放效果圖,DEMO在最後
想當年博主剛接觸Android的時候,看到這個效果心中只有膜拜啊,如果慢慢的自己水平也上來了,就把當年的一個想法給圓滿了吧。
好了,廢話不多說,先總結總結這個效果:
首先是需要自定義ListView,這點是必須的,然後在ListView的onTouchEvent方法中對事件進行處理普通的Item的話,是沒辦法實現這樣側滑的,即使你塞一個HorizontalScrollView進去都不行,所以也必須自定義一個ItemView實現左右側滑由於ListView的layout_width不一定是MATCH_PARENT,也可能是定值比如300dp,這個時候我們就需要建立一種機制來保證ItemView的寬度和ListView的寬度匹配,畢竟ItemView包含了兩個View,一個是正文的ContentView,一個是菜單的MenuView。 首先我從自定義ListView開始講起,這個ListView需要完成兩件事:事件分發和高度匹配。首先來看高度匹配:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); //寬度適配,改變ItemView的寬度 SlideItemView.Width = width; for(int i = 0; i < getChildCount(); i++){ SlideItemView item = (SlideItemView) getChildAt(i); item.resetWidth(); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
@Override public boolean onTouchEvent(MotionEvent ev) { float dx = 0; float dy = 0; switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: mTouchX = ev.getX(); mTouchY = ev.getY(); mMoveX = ev.getX(); mMoveY = ev.getY(); mTouchPosition = pointToPosition((int)ev.getX(), (int)ev.getY()); break; case MotionEvent.ACTION_MOVE: dx = ev.getX() - mMoveX; dy = ev.getY() - mMoveY; if(Math.abs(dx) > Math.abs(dy)){ //根據坐標點得到索引值 int position = pointToPosition((int)ev.getX(), (int)ev.getY()); if(mTouchPosition != ListView.INVALID_POSITION && position == mTouchPosition){ //得到內存中真實的Item SlideItemView itemView = (SlideItemView) getChildAt(position - getFirstVisiblePosition()); itemView.scroll((int) dx); } } mMoveX = ev.getX(); mMoveY = ev.getY(); break; case MotionEvent.ACTION_UP: dx = ev.getX() - mTouchX; dy = ev.getY() - mTouchY; if(Math.abs(dx) > Math.abs(dy) && Math.abs(dx) >= mTouchSlop){ int position = pointToPosition((int)ev.getX(), (int)ev.getY()); if(mTouchPosition != ListView.INVALID_POSITION && position == mTouchPosition){ //得到真正在內存中的Item SlideItemView itemView = (SlideItemView) getChildAt(position - getFirstVisiblePosition()); //根據當前scrollX以及dx判斷是否顯示正文內容 if (itemView.shouldShowContent((int) dx)){ itemView.showContent(); }else{ itemView.showMenu(); } }else if(position != mTouchPosition){ SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition()); //根據當前scrollX以及dx判斷是否顯示正文內容 if (itemView.shouldShowContent((int) dx)){ itemView.showContent(); }else{ itemView.showMenu(); } } }else{ SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition()); //根據當前scrollX以及dx判斷是否顯示正文內容 if (itemView.shouldShowContent((int) dx)){ itemView.showContent(); }else{ itemView.showMenu(); } } break; case MotionEvent.ACTION_CANCEL: if(mTouchPosition != ListView.INVALID_POSITION){ SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition()); itemView.showContent(); } break; } return super.onTouchEvent(ev); }
public void setView(SlideListView listView, int contentId, int menuId, float menuScale){ this.listView = listView; this.content = View.inflate(getContext(), contentId, null); this.menu = View.inflate(getContext(), menuId, null); this.scale = menuScale; LayoutParams param1 = new LayoutParams(Width, LayoutParams.MATCH_PARENT); addView(content, param1); LayoutParams param2 = new LayoutParams((int) (Width * menuScale), LayoutParams.MATCH_PARENT); addView(menu, param2); } public View getContent(){ return content; } public View getMenu(){ return menu; }
public void showContent(){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -mScroller.getFinalX(), 0); invalidate(); } public void showMenu(){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), menu.getWidth() - mScroller.getFinalX(), 0); invalidate(); } public boolean shouldShowContent(int dx){ //初始化 if(menu.getWidth() == 0){ resetWidth(); } if(dx > 0){ //右滑,當滑過1/4的時候開始變化 if(mScroller.getFinalX() < menu.getWidth() * 3 / 4){ return true; }else{ return false; } }else{ //左滑,當滑過1/4的時候開始變化 if(mScroller.getFinalX() < menu.getWidth() / 4){ return true; }else{ return false; } } }
public void scroll(int dx){ if(dx > 0){ //右滑 if(mScroller.getFinalX() > 0){ if(dx > mScroller.getFinalX()){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -mScroller.getFinalX(), 0); }else{ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0); } }else{ mScroller.setFinalX(0); } invalidate(); }else{ //左滑 if(mScroller.getFinalX() < menu.getWidth()){ if(mScroller.getFinalX() - dx > menu.getWidth()){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), menu.getWidth()- mScroller.getFinalX(), 0); }else{ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0); } }else{ mScroller.setFinalX(menu.getWidth()); } invalidate(); } }
/** * 重設寬度,在ListView的onMeasure方法中調用。 * 此方法是為了動態適配ListView的寬度,因為ListView的layout_width不一定等於MATCH_PARENT * 也可能是定值比如300dp */ public void resetWidth(){ ViewGroup.LayoutParams param1 = content.getLayoutParams(); if(param1 == null){ param1 = new LayoutParams(Width, LayoutParams.MATCH_PARENT); }else{ param1.width = Width; } content.setLayoutParams(param1); ViewGroup.LayoutParams param2 = menu.getLayoutParams(); if(param2 == null){ param2 = new LayoutParams((int) (Width * scale), LayoutParams.MATCH_PARENT); }else{ param2.width = (int) (Width * scale); } menu.setLayoutParams(param2); }
public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SlideListView listView = (SlideListView) findViewById(R.id.listView); listView.setAdapter(new SlideAdapter(this, listView)); } }
public class SlideAdapter extends BaseAdapter { private Context context; private SlideListView listView; public SlideAdapter(Context context, SlideListView listView){ this.context = context; this.listView = listView; } private String[] data = new String[]{ "1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231" }; @Override public int getCount() { return data.length; } @Override public Object getItem(int position) { return data[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){ SlideItemView itemView = new SlideItemView(context); itemView.setView(listView, R.layout.item_content, R.layout.item_menu, 2.0f / 3); holder = new ViewHolder(itemView); itemView.setTag(holder); convertView = itemView; }else{ holder = (ViewHolder) convertView.getTag(); } holder.textView.setText(data[position]); final SlideItemView itemView = (SlideItemView) convertView; holder.imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "點擊了imageview", Toast.LENGTH_SHORT).show(); itemView.showContent(); } }); holder.textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "點擊了textview", Toast.LENGTH_SHORT).show(); itemView.showContent(); } }); holder.btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "點擊了btn1", Toast.LENGTH_SHORT).show(); itemView.showContent(); } }); holder.btn2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context, "點擊了btn2", Toast.LENGTH_SHORT).show(); itemView.showContent(); } }); return convertView; } public class ViewHolder { public ImageView imageView; public TextView textView; public TextView btn1; public TextView btn2; public ViewHolder(SlideItemView view){ View content = view.getContent(); imageView = (ImageView) content.findViewById(R.id.imageView); textView = (TextView) content.findViewById(R.id.textView); View menu = view.getMenu(); btn1 = (TextView) menu.findViewById(R.id.btn1); btn2 = (TextView) menu.findViewById(R.id.btn2); } } }
item_menu.xml:
抽屜是用來放置安卓手機中所有需要顯示到Launcher上的(當然也可以進行過濾,將不想顯示的隱藏起來)應用和小部件,啟動應用、添加快捷方式到桌面、卸載等。之前也提到過,有
設置標題欄背景1> 准備背景圖片: background_pix.png注:用背景圖片比用顏色好處,可以讓背景看起來有凹凸感.2> drawable文件夾下放
推薦閱讀:使用RecyclerView添加Header和Footer的方法
在Android群裡,經常會有人問我,Android Log是怎麼用的,今天我就把從網上以及SDK裡東拼西湊過來,讓大家先一睹為快,希望對大家入門Android Log有