Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动
在上一篇文章《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的值)
有的时候我们虽然实现了一个功能或者某个逻辑,而如何才能使这段代码更加健壮和有效更值得我们去认真思考。。。
Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动的更多相关文章
- Android自定义组件系列【15】——四个方向滑动的菜单实现
今天无意中实现了一个四个方向滑动的菜单,感觉挺好玩,滑动起来很顺手,既然已经做出来了就贴出来让大家也玩弄一下. 一.效果演示 (说明:目前没有安装Android模拟器,制作的动态图片太卡了,就贴一下静 ...
- Android自定义组件系列【6】——进阶实践(3)
上一篇<Android自定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计划 ...
- Android自定义组件系列【5】——进阶实践(2)
上一篇<Android自定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一 ...
- Android自定义组件系列【7】——进阶实践(4)
上一篇<Android自定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识,这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpan ...
- Android自定义组件系列【3】——自定义ViewGroup实现侧滑
有关自定义ViewGroup的文章已经很多了,我为什么写这篇文章,对于初学者或者对自定义组件比较生疏的朋友虽然可以拿来主义的用了,但是要一步一步的实现和了解其中的过程和原理才能真真脱离别人的代码,举一 ...
- Android自定义组件系列【13】——Android自定义对话框如此简单
在我们的日常项目中很多地方会用到对话框,但是Android系统为我们提供的对话框样子和我们精心设计的界面很不协调,在这种情况下我们想很自由的定义对话框,或者有的时候我们的对话框是一个图片,没有标题和按 ...
- Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动
在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布 ...
- Android自定义组件系列【1】——自定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的所有特性,ViewGroup主要用来充当View的容器,将其中的View作为自己孩子,并对其进行管理,当然孩子也可以是ViewGr ...
- Android自定义组件系列【12】——非UI线程绘图SurfaceView
一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比 ...
随机推荐
- Spark通过YARN提交任务不成功(包含YARN cluster和YARN client)
无论用YARN cluster和YARN client来跑,均会出现如下问题. [spark@master spark-1.6.1-bin-hadoop2.6]$ jps 2049 NameNode ...
- element-UI实现el-table-column百分比自定义分配
1.把el-table-column的属性width换位min-width就支持百分比显示了.
- 【习题 7-10 Uva11214】Guarding the Chessboard
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 迭代加深搜索. 可以想见最后深度不会很深吧.. 然后皇后之间互相攻击到是允许的.. 就这样 [代码] /* 1.Shoud it u ...
- 【Codeforces Round #452 (Div. 2) B】Months and Years
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 闰,平,平 平,闰,平 平,平,闰 平,平,平 4种情况都考虑到就好. 可能有重复的情况. 但是没关系啦. [代码] #includ ...
- php函数,static,globalkeyword及三种变量作用域
函数 和js函数相似,关注php与其它语言不用的地方 函数的形參默认值 形式: function f($a,$b=3,$vc = "abc"){ } 注意:没有给默认值的形參必须传 ...
- Android学习笔记之滑动翻页(屏幕切换)
如何实现手机上手动滑动翻页效果呢?呵呵,在这里我们就给你们介绍一下吧. 一般实现这个特效会用到一个控件:ViewFlipper <1>View切换的控件—ViewFlipper 这个控件是 ...
- ORA-01665 control file is not a standby control file
ORA-01665错误处理 问题描述: 在备库启动至mount状态时,报如下错误: ORA-01665: control file is not a standby control file 解决办法 ...
- PatentTips - Method for guest operating system integrity validation
BACKGROUND The embodiments relate to guest operating system integrity validation, and more particula ...
- LeetCode Algorithm 07_Reverse Integer
Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 click to ...
- (转)bat批处理的注释语句
在批处理中,段注释有一种比较常用的方法: goto start = 可以是多行文本,可以是命令 = 可以包含重定向符号和其他特殊字符 = 只要不包含 :start 这一行,就都是注释 :start 另 ...