标签:
国际惯例,先来效果图
在阅读本文章之前,请确定熟悉【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]);
}
}
}Android自定义LinearLayout实现左右侧滑菜单,完美兼容ListView、ScrollView、ViewPager等滑动控件
标签:
原文地址:http://blog.csdn.net/cc_lova_wxf/article/details/51672482