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自己定义组件系列【7】——进阶实践(4)
上一篇<Android自己定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识.这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpa ...
- Android自己定义组件系列【6】——进阶实践(3)
上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计 ...
- Android自己定义组件系列【5】——进阶实践(2)
上一篇<Android自己定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这 ...
- Android自己定义组件系列【3】——自己定义ViewGroup实现側滑
有关自己定义ViewGroup的文章已经非常多了,我为什么写这篇文章,对于刚開始学习的人或者对自己定义组件比較生疏的朋友尽管能够拿来主义的用了,可是要一步一步的实现和了解当中的过程和原理才干真真脱离别 ...
- Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动
在上一篇文章<Android自定义组件系列[3]--自定义ViewGroup实现侧滑>中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示 ...
- Android自己定义组件系列【1】——自己定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGr ...
- Android自己定义组件系列【2】——Scroller类
在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实 ...
- Android自己定义组件系列【9】——Canvas绘制折线图
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了非常多插件,可是非常多时候我们须要依据详细项目自己定义这些图表,这一篇文章我们一起来看看怎样在Android中使用Can ...
- Android自己定义组件系列【5】——高级实践(1)
在接下来的几篇文章将任老师的博文<您可以下拉PinnedHeaderExpandableListView实现>骤来具体实现.来学习一下大神的代码并记录一下. 原文出处:http://blo ...
随机推荐
- 一个三年Android开发的总结-开篇
一个三年Android开发的总结-开篇 转眼间全职从事Android开发已有三年,想把这一阶段的积累总结记录并展现出来,作为Android开发必备的知识,希望对有一定Android开发基础的人进阶有裨 ...
- 【java项目实战】dom4j解析xml文件,连接Oracle数据库
简单介绍 dom4j是由dom4j.org出品的一个开源XML解析包.这句话太官方.我们还是看一下官方给出的解释.例如以下图: dom4j是一个易于使用的.开源的,用于解析XML,XPath和XSLT ...
- mbed
mbed介绍--ARM最新面向IOT的RTOS与平台 文章为本人原创,转载请注明:http://blog.csdn.net/guo8113/article/details/40479303 mbed是 ...
- 启用QNX系统,海尔智能冰箱或成业界“宝马”
智能家电正处于迅猛发展的态势,国内眼下有非常多企业都在积极布局智能家电,当中又以海尔最为典型.作为家电领域的领头羊,海尔近年来在智能家电领域的动作不小.近期有消息透露.海尔也许会在IFA展会上 ...
- [寒江孤叶丶的Cocos2d-x之旅_33]RichTextEx一款通过HTML标签控制文字样式的富文本控件
RichTextEx一款通过HTML标签控制文字样式的富文本控件 原创文章,欢迎转载.转载请注明:文章来自[寒江孤叶丶的Cocos2d-x之旅系列] 博客地址:http://blog.csdn.net ...
- ios 导航栏 点击barbutton的按钮 下拉列表
环境:xocde5.0.2+ios7.0.1 1.导航栏 ----点击科目--------下拉列表 代码:NGRightTableViewViewController.h #import <UI ...
- phpstudy配置多个域名
phpstudy配置多个域名 1.端口配置对应的文件为PHPTutorial\Apache\conf\httpd.conf 直接在这个文件里面搜索localhost就可以搜到上图对应的代码那一块 第一 ...
- angular4父组件向子组件传值,子组件向父组件传值的方法
父组件向子组件传值 @Input 文件目录 父组件: father.template.html <h1>父组件</h1> <cmt-child [data]='dat ...
- 基于Socket的Winform例子
一.直接上效果图 二.Socket握手 三.服务端 Thread threadWatch = null;// 负责监听客户端的线程 Socket socketWatch = null;// 负责监听客 ...
- Linux中安装redis的phpredis扩展
下载phpredis扩展 http://pecl.php.net/package/redis wget http://pecl.php.net/get/redis-4.0.2.tgz 需要环境配置文件 ...