您的位置:首页 > 移动开发 > Android开发

Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动

2014-04-22 13:41 806 查看
在上一篇文章《Android自定义组件系列【3】——自定义ViewGroup实现侧滑》中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果。

1、布局示意图:






2、核心代码



@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);  //获取MyScrollView的宽度
mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
if(!isLocked){
initX = getScrollX();
isLocked = true;
}
}
在该方法中获取到初始的视图坐标偏移量getScrollX()


@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("ACTION_DOWN");
mDownX = x;          //记录按下时的x坐标
break;
case MotionEvent.ACTION_UP:
System.out.println("ACTION_UP");
int dis = (int) (x - mDownX);   //滑动的距离
if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
if(dis > 0){          //如果>0则是向右滑动
toRightMove();
}else{				  //如果<0则是向左滑动
toLeftMove();
}
}
break;
default:
break;
}

return true;
}
监听函数记录下按下和移动的屏幕坐标,求差计算出移动距离,如果这个距离大于阀值 (mWidth * mMenuWeight / 2)则滑动
public void toRightMove(){
System.out.println("maxRight = " + maxRight);
System.out.println("X = "  + getScrollX());
if(getScrollX() >= initX){
int dx = (int)(mWidth * mMenuWeight);
mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
if(mListener != null){
mListener.onChanged();
}
invalidate();
}
}
如果是向右滑动则,如果当前是初始位置(centerView在中间)则可以向右滑动(getScrollX == initX),或者当前左边View可以看见,则可以向右滑动将centerView移动到中间(getScrollX > initX).同理有向左滑动的方法。
public void toLeftMove(){
System.out.println("maxLeft = " + maxLeft);
System.out.println("X = "  + getScrollX());
if(getScrollX() <= initX){
int dx = (int)(mWidth * mMenuWeight);
mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
if(mListener != null){
mListener.onChanged();
}
invalidate();
}
}
3、全部代码
MyScrollView.java



package com.example.testscrollto;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Scroller;

public class MyScrollView extends LinearLayout{

private Context mContext;
private int mWidth;
private int mHeight;

private float mMenuWeight = 3.0f / 5; //菜单界面比例

private View mMenuView;   //菜单界面
private View mPriView;	  //内容界面
private View mRightView;  //右边界面

private int maxLeft;
private int maxRight;
private int initX;

private boolean isLocked = false;

private Scroller mScroller;

private OnMenuChangedListener mListener;

public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mScroller = new Scroller(mContext);

}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mMenuView.layout(-(int)(mWidth * mMenuWeight), 0, 0, mHeight);
mPriView.layout(0, 0, mWidth, mHeight);
mRightView.layout(mWidth, 0, mWidth + (int)(mWidth * mMenuWeight), mHeight);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);  //获取MyScrollView的宽度
mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
if(!isLocked){
initX = getScrollX();
isLocked = true;
}
}

/**设置右滑的菜单View*/
public void setMenu(View menu){
mMenuView = menu;
addView(mMenuView);
}

/**
* 设置主界面View
*/
public void setPrimary(View primary){
mPriView = primary;
addView(mPriView);
}

public void setRightView(View rightview){
mRightView = rightview;
addView(mRightView);
}

private float mDownX;

@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("ACTION_DOWN");
mDownX = x;          //记录按下时的x坐标
break;
case MotionEvent.ACTION_UP:
System.out.println("ACTION_UP");
int dis = (int) (x - mDownX);   //滑动的距离
if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
if(dis > 0){          //如果>0则是向右滑动
toRightMove();
}else{				  //如果<0则是向左滑动
toLeftMove();
}
}
break;
default:
break;
}

return true;
}

@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}

public void toRightMove(){
System.out.println("maxRight = " + maxRight);
System.out.println("X = "  + getScrollX());
if(getScrollX() >= initX){
int dx = (int)(mWidth * mMenuWeight);
mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
if(mListener != null){
mListener.onChanged();
}
invalidate();
}
}

public void toLeftMove(){
System.out.println("maxLeft = " + maxLeft);
System.out.println("X = "  + getScrollX());
if(getScrollX() <= initX){
int dx = (int)(mWidth * mMenuWeight);
mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
if(mListener != null){
mListener.onChanged();
}
invalidate();
}
}

public interface OnMenuChangedListener{
public void onChanged();
}

public void setOnMenuChangedListener(OnMenuChangedListener listener){
mListener = listener;
}
}
MainActivity.java
package com.example.testscrollto;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

import com.example.testscrollto.MyScrollView.OnMenuChangedListener;

public class MainActivity extends Activity {

private MyScrollView mScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScrollView = (MyScrollView)findViewById(R.id.rightscrollview);
final View menu = getLayoutInflater().inflate(R.layout.rightscrollview_menu, null);
final View primary = getLayoutInflater().inflate(R.layout.rightscrollview_primary, null);
final View rightview = getLayoutInflater().inflate(R.layout.rightscrollview_right_menu, null);
mScrollView.setMenu(menu);
mScrollView.setPrimary(primary);
mScrollView.setRightView(rightview);
mScrollView.setOnMenuChangedListener(new OnMenuChangedListener() {

@Override
public void onChanged() {
System.out.println("窗口切换了一次");
}
});
}
}
activity_main.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:orientation="vertical" >

<com.example.testscrollto.MyScrollView
android:id="@+id/rightscrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>
其余三个视图界面无限制,可以自由定义,这里就不贴出来了。
4、运行效果:







源代码下载:http://download.csdn.net/detail/lxq_xsyu/7232701

这样就可以实现左右滑动了,没有任何bug吗?

其实这样看似是没有什么问题了,上面用于判断界面位置的代码在逻辑上看似是对的,但是在实际的应用中偶尔会出现错乱。

下面我们用另外一种方式解决:

package com.example.jaohangui.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;

public class MyScrollLeftRightView extends LinearLayout{

private Scroller mScroller;

private View mLeftView;  //坐标界面
private View mMainView;  //中间主界面
private View mRightView; //右边界面

private float mMeasureWight = 3.0f / 5; //菜单界面比例
private int mWidth;
private int mHeight;

private boolean isLocked = false;
private boolean isToLeft = false;
private static int CENTER_PAGE = 1;
private static int LEFT_PAGE = 0;
private static int RIGHT_PAGE = 2;
private int currentPage = CENTER_PAGE;

public MyScrollLeftRightView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mLeftView.layout(-(int)(mWidth * mMeasureWight), 0, 0, mHeight);
mMainView.layout(0, 0, mWidth, mHeight);
mRightView.layout(mWidth, 0, mWidth + (int)(mWidth * mMeasureWight), mHeight);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}

/**
* 添加左边界面内容
* @param view
*/
public void setLeftView(View view){
mLeftView = view;
addView(mLeftView);
}

/**
* 添加主界面内容
* @param view
*/
public void setMainView(View view){
mMainView = view;
addView(mMainView);
}

/**
* 添加右边界面内容
* @param view
*/
public void setRightView(View view){
mRightView = view;
addView(mRightView);
}

private float mDownX;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = x;
break;
case MotionEvent.ACTION_UP:
int dis = (int)(x - mDownX); //滑动的距离
if(Math.abs(dis) > (mWidth * mMeasureWight / 3)){
if(dis > 0){
toRightMove();
}else{
toLeftMove();
}
}
break;

default:
break;
}
return true;
}

@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
isLocked = true;
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}else{
if(currentPage == CENTER_PAGE){
if(isToLeft){
currentPage = RIGHT_PAGE;
}else{
currentPage = LEFT_PAGE;
}
}else{
currentPage = CENTER_PAGE;
}
isLocked = false;
}
}

public void toRightMove(){
if(currentPage == LEFT_PAGE || isLocked){
return;
}
int dx = (int)(mWidth * mMeasureWight);
mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
invalidate();
isToLeft = false;
}

public void toLeftMove(){
if(currentPage == RIGHT_PAGE || isLocked){
return;
}
System.out.println("ok");
int dx = (int)(mWidth * mMeasureWight);
mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
invalidate();
isToLeft = true;
}
}


上面使用了两个用来判断的变量和一个锁定状态(不让进入toLeftMove和toRightMove)的变量。

1、在进入toLeftMove或者toRightMove方法的时候首先会判断是否isLocked为true,如果为true则说明当前是正在滑动状态,不可以执行这两个方法。如果不这样去控制,在界面正在滑动的时候上面的currentPage就会发生错乱。

2、将要滑动之前告诉computeScroll()方法,是从toLeftMove和toRightMove的那个方法中出来的(使用isToLeft)。

3、最后判断Scroller是否已经停止滑动(移动),如果停止则改变当前页面的状态(currentPage的值)

有的时候我们虽然实现了一个功能或者某个逻辑,而如何才能使这段代码更加健壮和有效更值得我们去认真思考。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: