自定义控件(ScrollView的重写)

版权声明:欢迎转载,转载请注明出处http://blog.csdn.net/qian_xiao_lj

实现两个ScrollView的同步显示。因为Android控件中没有此种功能,因此需要重写ScrollView。

思路介绍
  我们首先想到使用ScrollView的类似与setOnScrollChangedListener的方法来实现,当一个ScrollView滚动时,触发该方法进而使另外一个ScrollView滚动。不过很遗憾,谷歌没有提供该方法。通过查询相应的源代码,我们发现该方法的原型
protected void onScrollChanged(int x, int y, int oldx, int oldy)  
该方法是protected类型,不能直接调用,于是需要重新实现ScrollView。
具体实现
  首先,定一个一个接口(ScrollViewListener.java):
public interface ScrollViewListener {  
  
    void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);  
  
}  
我们需要重写ScrollView才能实现该接口,因此有下面的代码(ObservableScrollView.java):

import android.content.Context;  
import android.util.AttributeSet;  
import android.widget.ScrollView;  
  
public class ObservableScrollView extends ScrollView {  
  
    private ScrollViewListener scrollViewListener = null;  
  
    public ObservableScrollView(Context context) {  
        super(context);  
    }  
  
    public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
    }  
  
    public ObservableScrollView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public void setScrollViewListener(ScrollViewListener scrollViewListener) {  
        this.scrollViewListener = scrollViewListener;  
    }  
  
    @Override  
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {  
        super.onScrollChanged(x, y, oldx, oldy);  
        if(scrollViewListener != null) {  
            scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);  
        }  
    }  
  
}  
接下来是界面的XML,这里是一个简单的Demo,如下(main.xml):

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:background="#ffffff"  
    android:orientation="horizontal" >  
  
    <com.devin.ObservableScrollView  
        android:id="@+id/scrollview1"  
        android:layout_width="400dp"  
        android:layout_height="wrap_content" >  
  
        <LinearLayout  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:orientation="vertical" >  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="monday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="tuesday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="wednesday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="thursday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="friday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="saturday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="sunday"  
                android:textColor="#000000" />  
        </LinearLayout>  
    </com.devin.ObservableScrollView>  
  
    <com.devin.ObservableScrollView  
        android:id="@+id/scrollview2"  
        android:layout_width="400dp"  
        android:layout_height="wrap_content" >  
  
        <LinearLayout  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:orientation="vertical" >  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="monday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="tuesday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="wednesday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="thursday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="friday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="saturday"  
                android:textColor="#000000" />  
  
            <TextView  
                android:layout_width="wrap_content"  
                android:layout_height="200dp"  
                android:layout_weight="1"  
                android:text="sunday"  
                android:textColor="#000000" />  
        </LinearLayout>  
    </com.devin.ObservableScrollView>  
  
</LinearLayout>
最后是我们的主程调用(TestActivity.java):

<strong style="font-size: 14px;">import android.app.Activity;  
import android.os.Bundle;  
  
public class TestActivity extends Activity implements ScrollViewListener {  
  
    private ObservableScrollView scrollView1 = null;  
    private ObservableScrollView scrollView2 = null;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1);  
        scrollView1.setScrollViewListener(this);  
        scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2);  
        scrollView2.setScrollViewListener(this);  
    }  
  
    public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {  
        if(scrollView == scrollView1) {  
            scrollView2.scrollTo(x, y);  
        } else if(scrollView == scrollView2) {  
            scrollView1.scrollTo(x, y);  
        }  
    }  
  
}  
 </strong><p style="font-size: 16px; font-weight: bold; margin-top: 0px; margin-bottom: 0.75em; line-height: 27.2000007629395px; text-indent: 1em; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, STXihei, 'Microsoft YaHei', 微软雅黑, sans-serif; background-color: rgb(254, 254, 254);">可嵌套的<span style="line-height: 27.2000007629395px;">ScrollView:</span></p><p style="font-size: 16px; font-weight: bold; margin-top: 0px; margin-bottom: 0.75em; line-height: 27.2000007629395px; text-indent: 1em; font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, STXihei, 'Microsoft YaHei', 微软雅黑, sans-serif; background-color: rgb(254, 254, 254);"></p><p align="left" style="font-size: 14px;"><span style="color: rgb(68, 68, 68);">实现一个继承自ScrollView</span>的自定义控件,可以设置坐标,大小,并且能够任意添加其他的view。由于ScrollView只允许有一个子控件,所以,我们可以在其中加入一个ViewGroup,然后,将要添加的view加入该ViewGroup中即可</p><p align="left" style="font-size: 14px;">下面是该类的代码<span style="font-weight: bold;">:</span></p><p align="left" style="font-size: 14px; font-weight: bold;"></p><pre name="code" class="java" style="font-size: 14px; font-weight: bold;">public class LionScrollView extends ScrollView {
  private MainViewGroup mv; 
  private int subViewWidth = 0;
  private int subViewHeight = 0; 
  private int x;
  private int y; 
  private int width;
private int height;
public LionScrollView() {
super(MainView.mMainActivity);
this.mv = new MainViewGroup();
super.addView(mv);
}
public void addView(View child) {
if (child == null)
return;
this.mv.addView(child);
}
//This method should return false to intercept the touch event
public boolean onInterceptTouchEvent(MotionEvent ev) {
 return false;
  }
//here you can add some setters
.....
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
  final int count = this.mv.getChildCount();
  for (int i = 0; i < count; i++) { 
  final View child = this.mv.getChildAt(i);
  int x = (int) child.getX() + child.getMeasuredWidth(); 
  int y = (int) child.getY() + child.getMeasuredHeight(); 
if (x > subViewWidth) { 
  subViewWidth = x; this.mv.setMinimumWidth(x);
  }
 if (y > subViewHeight) { 
  subViewHeight = y; this.mv.setMinimumHeight(y);
  } 
 } 
 this.setLeft(this.x); 
 this.setRight(this.x + this.width);
 this.setTop(this.y);
 this.setBottom(this.y + this.height);
 super.onLayout(changed, l, t, r, b);
 }
}


横向ScrollView:

 
 

自定义Viewiw实现步骤:

1、继承要自定义View的类,并实现带有参数的构造方法

2、重写onMeasure(确定自定义View的大小)和onLayout(确定自定义View的位置)方法

关于HorizontalScrollView的滑动,我们可以用onScrollChanged来监听参数L:

打印日志时可以发现,当滚动条向左(画面向右滑动)的时候,L的值是逐渐增大的,所以我们可以通过它来作为动画的变化梯度值。

HorizontalScrollView.class

public class MyHorizontalScrollView extends HorizontalScrollView {
    // 在HorizontalScrollView有个LinearLayout
     private LinearLayout linearLayout;
      // 菜单,内容页
      private ViewGroup myMenu;
      private ViewGroup myContent;
      //菜单宽度
      private int myMenuWidth;
  
      // 屏幕宽度
      private int screenWidth;
      // 菜单与屏幕右侧的距离(dp)
      private int myMenuPaddingRight = 50;
  
      // 避免多次调用onMeasure的标志
      private boolean once = false;
  
      /**
       * 自定义View需要实现带有Context、AttributeSet这2个参数的构造方法,否则自定        义参数会出错
       * 当使用了自定义属性时,会调用此构造方法
       * 
       * @param context
       * @param attrs
       */
      public MyHorizontalScrollView(Context context, AttributeSet attrs) {
          super(context, attrs);
          // 获取屏幕宽度
          WindowManager windowManager = (WindowManager) context
                  .getSystemService(Context.WINDOW_SERVICE);
          DisplayMetrics outMetrics = new DisplayMetrics();
          windowManager.getDefaultDisplay().getMetrics(outMetrics);
          screenWidth = outMetrics.widthPixels;// 屏幕宽度
  
          // 将dp转换px
          myMenuPaddingRight = (int) TypedValue.applyDimension(
                  TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources()
                          .getDisplayMetrics());
  
      }
 
     /**
       * 设置子View的宽高,决定自身View的宽高,每次启动都会调用此方法
      */
    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (!once) {//使其只调用一次
              // this指的是HorizontalScrollView,获取各个元素
              linearLayout = (LinearLayout) this.getChildAt(0);// 第一个子元素
              myMenu = (ViewGroup) linearLayout.getChildAt(0);// HorizontalScrollView下LinearLayout的第一个子元素
              myContent = (ViewGroup) linearLayout.getChildAt(1);// HorizontalScrollView下LinearLayout的第二个子元素
 
             // 设置子View的宽高,高于屏幕一致
             myMenuWidth=myMenu.getLayoutParams().width = screenWidth - myMenuPaddingRight;// 菜单的宽度=屏幕宽度-右边距
             myContent.getLayoutParams().width = screenWidth;// 内容宽度=屏幕宽度
              // 决定自身View的宽高,高于屏幕一致
              // 由于这里的LinearLayout里只包含了Menu和Content所以就不需要额外的去指定自身的宽
              once = true;
         }
     }

      //设置View的位置,首先,先将Menu隐藏(在eclipse中ScrollView的画面内容(非滚动条)正数表示向左移,向上移)
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
         //刚载入界面的时候隐藏Menu菜单也就是ScrollView向左滑动菜单自身的大小
          if(changed){
             this.scrollTo(myMenuWidth, 0);//向左滑动,相当于把右边的内容页拖到正中央,菜单隐藏    
          }
     
     }
     
      @Override
     public boolean onTouchEvent(MotionEvent ev) {
        int action=ev.getAction();
          switch (action) {
          case MotionEvent.ACTION_UP:
              int scrollX=this.getScrollX();//滑动的距离scrollTo方法里,也就是onMeasure方法里的向左滑动那部分
             if(scrollX>=myMenuWidth/2){
                  this.smoothScrollTo(myMenuWidth,0);//向左滑动展示内容
             }else{
                  this.smoothScrollTo(0, 0);
             }
             return true;
        }
         return super.onTouchEvent(ev);
     }
     
     
     @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
         Log.i("tuzi",l+"");
        float scale = l * 1.0f / myMenuWidth; // 1 ~ 0

        /**
         * 区别1:内容区域1.0~0.7 缩放的效果 scale : 1.0~0.0 0.7 + 0.3 * scale
         * 
          * 区别2:菜单的偏移量需要修改
         * 
          * 区别3:菜单的显示时有缩放以及透明度变化 缩放:0.7 ~1.0 1.0 - scale * 0.3 透明度 0.6 ~ 1.0 
         * 0.6+ 0.4 * (1- scale) ;
          * 
         */
         float rightScale = 0.7f + 0.3f * scale;
        float leftScale = 1.0f - scale * 0.3f;
        float leftAlpha = 0.6f + 0.4f * (1 - scale);
        // 调用属性动画,设置TranslationX
       ViewHelper.setTranslationX(myMenu, myMenuWidth * scale * 0.8f);
        
        ViewHelper.setScaleX(myMenu, leftScale);
         ViewHelper.setScaleY(myMenu, leftScale);
        ViewHelper.setAlpha(myMenu, leftAlpha);
        // 设置content的缩放的中心点
        ViewHelper.setPivotX(myContent, 0);
       ViewHelper.setPivotY(myContent, myContent.getHeight() / 2);
         ViewHelper.setScaleX(myContent, rightScale);
        ViewHelper.setScaleY(myContent, rightScale);
     }

}






 
 

你可能感兴趣的:(自定义控件)