国际惯例,先来效果图
在阅读本文章之前,请确定熟悉【Scroller】相关的知识,如果不熟悉,请小伙伴儿先百度后再来吧。
假如你已经知道【Scroller】了,那么就接着往下看吧。
首先,我们把侧拉菜单的构造给解析出来。多次观看上面的效果图,我们可以得出以下的结论。
/** * Created by ccwxf on 2016/6/14. */ public abstract class UI { protected Context context; //当前UI界面的布局文件 protected View contentView; //当前UI界面在父控件的起点X坐标 protected int startX; //当前UI界面在父控件的终点X坐标 protected int stopX; //当前UI界面的宽度 protected int width; protected UI(Context context, View contentView){ this.context = context; this.contentView = contentView; } protected abstract void calculate(float leftScale, float rightScale); protected void show(Scroller mScroller){ if(mScroller != null){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), startX - mScroller.getFinalX(), 0); } } }
/** * Created by ccwxf on 2016/6/14. */ public class LeftMenuUI extends UI { // 是指要打开该UI界面所需要滚动的X坐标临界值 public int openX; // 是指要关闭该UI界面所需要的滚动的X坐标临界值 public int closeX; public LeftMenuUI(Context context, View contentView) { super(context, contentView); } @Override protected void calculate(float leftScale, float rightScale) { startX = 0; stopX = (int) (Util.getScreenWidth(context) * leftScale); this.width = stopX - startX; this.openX = (int) (startX + (1 - SideLayout.DEFAULT_SIDE) * this.width); this.closeX = (int) (startX + SideLayout.DEFAULT_SIDE * this.width); } }
public class ContentUI extends UI { public ContentUI(Context context, View contentView) { super(context, contentView); } @Override protected void calculate(float leftScale, float rightScale) { int width = Util.getScreenWidth(context); int leftWidth = (int) (width * leftScale); startX = leftWidth; stopX = leftWidth + width; this.width = stopX - startX; } }
/** * Created by ccwxf on 2016/6/14. */ public class RightMenuUI extends UI { // 是指要打开该UI界面所需要滚动的X坐标临界值 public int openX; // 是指要关闭该UI界面所需要的滚动的X坐标临界值 public int closeX; public RightMenuUI(Context context, View contentView) { super(context, contentView); } @Override protected void calculate(float leftScale, float rightScale) { int width = Util.getScreenWidth(context); startX = (int) (width * (1 + leftScale)); stopX = (int) (width * (1 + leftScale + rightScale)); this.width = stopX - startX; this.openX = (int) (startX - width + SideLayout.DEFAULT_SIDE * this.width); this.closeX = (int) (startX - width + (1 - SideLayout.DEFAULT_SIDE) * this.width); } /** * 必须重载父类方法,因为滑动的起点是从0开始 */ protected void show(Scroller mScroller, int measureWidth){ if(mScroller != null){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), measureWidth - Util.getScreenWidth(context) - mScroller.getFinalX(), 0); } } }
/** * Created by ccwxf on 2016/6/14. */ public class SideLayout extends LinearLayout { //默认的菜单宽度与屏幕宽度的比值 public static final float DEFAULT_SCALE = 0.66f; //默认的滑动切换阀值相对于菜单宽度的比值 public static final float DEFAULT_SIDE = 0.25f; private Scroller mScroller; //三个UI界面 private LeftMenuUI leftMenuUI; private ContentUI contentUI; private RightMenuUI rightMenuUI; //左菜单和右菜单相对于屏幕的比值 private float leftScale = 0; private float rightScale = 0; //控件的测量宽度 private float measureWidth = 0; //手指Touch时的X坐标和移动时的X坐标 private float mTouchX; private float mMoveX; public SideLayout(Context context) { super(context); init(); } public SideLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SideLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mScroller = new Scroller(getContext()); setOrientation(LinearLayout.HORIZONTAL); } /** * 设置左菜单的布局 * @param view 左菜单布局 * @return 返回当类 */ public SideLayout setLeftMenuView(View view){ return setLeftMenuView(view, DEFAULT_SCALE); } public SideLayout setLeftMenuView(View view, float leftScale){ leftMenuUI = new LeftMenuUI(getContext(), view); this.leftScale = leftScale; return this; } /** * 设置右菜单的布局 * @param view 右菜单布局 * @return 当类 */ public SideLayout setRightMenuView(View view){ return setRightMenuView(view, DEFAULT_SCALE); } public SideLayout setRightMenuView(View view, float rightScale){ rightMenuUI = new RightMenuUI(getContext(), view); this.rightScale = rightScale; return this; } /** * 设置正文布局 * @param view 正文布局 * @return 返回当类 */ public SideLayout setContentView(View view){ contentUI = new ContentUI(getContext(), view); return this; } /** * 提交配置,必须调用 */ public void commit() { removeAllViews(); if(leftMenuUI != null){ leftMenuUI.calculate(leftScale, rightScale); measureWidth += leftMenuUI.width; addView(leftMenuUI.contentView, new LayoutParams(leftMenuUI.width, LayoutParams.MATCH_PARENT)); } if(contentUI != null){ contentUI.calculate(leftScale, rightScale); measureWidth += contentUI.width; addView(contentUI.contentView, new LayoutParams(contentUI.width, LayoutParams.MATCH_PARENT)); } if(rightMenuUI != null){ rightMenuUI.calculate(leftScale, rightScale); measureWidth += rightMenuUI.width; addView(rightMenuUI.contentView, new LayoutParams(rightMenuUI.width, LayoutParams.MATCH_PARENT)); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mTouchX = event.getX(); mMoveX = event.getX(); return true; case MotionEvent.ACTION_MOVE: int dx = (int) (event.getX() - mMoveX); if(dx > 0){ //右滑 if(mScroller.getFinalX() > 0){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0); }else{ mScroller.setFinalX(0); } }else{ //左滑 if(mScroller.getFinalX() + Util.getScreenWidth(getContext()) - dx < measureWidth){ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0); }else{ mScroller.setFinalX((int) (measureWidth - Util.getScreenWidth(getContext()))); } } mMoveX = event.getX(); invalidate(); break; case MotionEvent.ACTION_UP: toTargetUI((int) (event.getX() - mTouchX)); break; } return super.onTouchEvent(event); } /** * 滑动切换到目标的UI界面 * @param dx 手指抬起时相比手指落下,滑动的距离 */ private void toTargetUI(int dx){ int scrollX = mScroller.getFinalX(); if(dx > 0){ //右滑 if(leftMenuUI != null){ if(scrollX >= leftMenuUI.openX && scrollX < leftMenuUI.stopX){ contentUI.show(mScroller); }else if(scrollX >= leftMenuUI.startX && scrollX < leftMenuUI.openX){ leftMenuUI.show(mScroller); } } if(rightMenuUI != null){ if(scrollX >= rightMenuUI.closeX){ rightMenuUI.show(mScroller, (int) measureWidth); }else if(scrollX >= contentUI.startX && scrollX < rightMenuUI.closeX){ contentUI.show(mScroller); } } }else{ //左滑 if(leftMenuUI != null){ if(scrollX > leftMenuUI.startX && scrollX <= leftMenuUI.closeX){ leftMenuUI.show(mScroller); }else if(scrollX > leftMenuUI.closeX && scrollX < leftMenuUI.stopX){ contentUI.show(mScroller); } } if(rightMenuUI != null){ if(scrollX > contentUI.startX && scrollX <= rightMenuUI.openX){ contentUI.show(mScroller); }else if(scrollX > rightMenuUI.openX){ rightMenuUI.show(mScroller, (int) measureWidth); } } } } @Override public void computeScroll(){ if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } super.computeScroll(); } }
/** * 滑动切换到目标的UI界面 * @param dx 手指抬起时相比手指落下,滑动的距离 */ private void toTargetUI(int dx){ int scrollX = mScroller.getFinalX(); if(dx > 0){ //右滑 if(leftMenuUI != null){ if(scrollX >= leftMenuUI.openX && scrollX < leftMenuUI.stopX){ contentUI.show(mScroller); }else if(scrollX >= leftMenuUI.startX && scrollX < leftMenuUI.openX){ leftMenuUI.show(mScroller); } } if(rightMenuUI != null){ if(scrollX >= rightMenuUI.closeX){ rightMenuUI.show(mScroller, (int) measureWidth); }else if(scrollX >= contentUI.startX && scrollX < rightMenuUI.closeX){ contentUI.show(mScroller); } } }else{ //左滑 if(leftMenuUI != null){ if(scrollX > leftMenuUI.startX && scrollX <= leftMenuUI.closeX){ leftMenuUI.show(mScroller); }else if(scrollX > leftMenuUI.closeX && scrollX < leftMenuUI.stopX){ contentUI.show(mScroller); } } if(rightMenuUI != null){ if(scrollX > contentUI.startX && scrollX <= rightMenuUI.openX){ contentUI.show(mScroller); }else if(scrollX > rightMenuUI.openX){ rightMenuUI.show(mScroller, (int) measureWidth); } } } }
/** * Created by ccwxf on 2016/6/14. */ public class Util { public static int getScreenWidth(Context context){ return context.getResources().getDisplayMetrics().widthPixels; } }
<?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:background="@android:color/holo_red_light" android:orientation="vertical"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content" android:listSelector="@android:color/transparent" android:cacheColorHint="@android:color/transparent" android:dividerHeight="0dp" android:divider="@null" /> </LinearLayout>
<?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:background="@android:color/white" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="300dp"/> </LinearLayout>
<?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:background="@android:color/holo_blue_light" android:orientation="vertical"> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:layout_width="300dp" android:layout_height="300dp" android:background="@android:color/holo_green_light" /> <TextView android:layout_width="300dp" android:layout_height="300dp" android:background="@android:color/darker_gray" /> <TextView android:layout_width="300dp" android:layout_height="300dp" android:background="@android:color/holo_orange_light" /> </LinearLayout> </HorizontalScrollView> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <cc.wxf.side.SideLayout android:id="@+id/sideLayout" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
public class MainActivity extends Activity { private View leftView; private View contentView; private View rightView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); SideLayout sideLayout = (SideLayout) findViewById(R.id.sideLayout); sideLayout.setLeftMenuView(leftView).setContentView(contentView).setRightMenuView(rightView).commit(); } private void initUI(){ leftView = View.inflate(this, R.layout.left, null); contentView = View.inflate(this, R.layout.content, null); rightView = View.inflate(this, R.layout.right, null); //初始化左边菜单 ListView listView = (ListView) leftView.findViewById(R.id.listView); listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new String[]{ "123","456","789","101","112","123","456","789","101","112","123","456","789","101","112","123","456","789","101","112" })); //初始化正文内容 ViewPager viewPager = (ViewPager) contentView.findViewById(R.id.viewPager); viewPager.setAdapter(new TestDemoAdapter()); } public class TestDemoAdapter extends PagerAdapter{ private ImageView[] imageViews = new ImageView[5]; public TestDemoAdapter() { for(int i = 0; i < imageViews.length; i++){ imageViews[i] = new ImageView(MainActivity.this); imageViews[i].setImageResource(R.mipmap.ic_launcher); } } @Override public int getCount() { return 5; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(imageViews[position]); return imageViews[position]; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(imageViews[position]); } } }