zoukankan      html  css  js  c++  java
  • Android滚轮选择器实现

    思路:

    1。布局,整个控件的布局,事实上就是用代码取带xml来实现当前布局

    2,能够滑动的(即滚轮)。事实上是一个ScrollView

    3。推断滑动状态的,有protected void onScrollChanged(int x, int y, int oldx, int oldy) 方法,能够为我们获得当前y值(一開始y=0;随着滑动。y開始增大)

        那么我们首先来完毕第一个,为了思考方便,我先用xml搭建出了控件的样子,然后我们再用代码去实现,事实证明,这种思路行云流水

    以下,我们来看这个test.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!-- 这个布局文件仅仅是为了思考方便的。实际上并不须要用到,真正在布局的,是在main.xml里面添加自己定义控件 -->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin" >
    
        <RelativeLayout
            android:id="@+id/relativeLayout1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/act_menu_bg_hover" >
    
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:layout_marginTop="100dp"
                android:background="@drawable/shoukuan_border4" />
    
            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="300dp"
                android:orientation="horizontal" >
    
                <RelativeLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="4" >
    
                    <RelativeLayout
                        android:layout_width="wrap_content"
                        android:layout_height="fill_parent"
                        android:gravity="center" >
    
                        <TextView
                            android:id="@+id/aa"
                            android:layout_width="wrap_content"
                            android:layout_height="fill_parent"
                            android:layout_alignParentRight="true"
                            android:gravity="center_vertical"
                            android:text="单位" />
    
                        <ScrollView
                            android:layout_width="fill_parent"
                            android:layout_height="300dp"
                            android:layout_centerHorizontal="true"
                            android:layout_toLeftOf="@id/aa"
                            android:overScrollMode="never"
                            android:scrollbars="none" >
    
                            <LinearLayout
                                android:layout_width="wrap_content"
                                android:layout_height="300dp"
                                android:layout_gravity="center_horizontal"
                                android:orientation="vertical" >
    
                                <!--
    					    	 <TextView 
                         	        android:layout_height="100dp"
                         	        android:layout_width="wrap_content"
                         	        android:textAlignment="center"
                         	        android:text=""/>
                         	    <TextView 
                         	        android:layout_height="100dp"
                         	        android:layout_width="wrap_content"
                         	        android:textAlignment="center"
                         	        android:gravity="center_vertical"                     	                             	        
                         	        android:text="1999"/>
    
                         	      <TextView
                         	          android:layout_width="wrap_content"
                         	          android:layout_height="100dp"
                         	          android:text="2000"
                         	          android:gravity="center_vertical"       
                         	          android:textAlignment="center" />
    
                         	        <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2001"/>
                         	          <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2002"/>
                         	            <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2003"/>
                         	              <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2004"/>
                         	                <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2005"/>
                         	                  <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:text="2006"/>
                         	                  <TextView 
                         	        android:layout_height="100dp"
                         	        android:gravity="center_vertical"       
                         	        android:layout_width="wrap_content"
                         	        android:textAlignment="center"
                         	        android:text=""/>
                                -->
                            </LinearLayout>
                        </ScrollView>
                    </RelativeLayout>
                </RelativeLayout>
    
                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_weight="3"
                    android:background="@drawable/btton_avtie"
                    android:text="确定"
                    android:textSize="30dp" />
            </LinearLayout>
        </RelativeLayout>
    
    </RelativeLayout>

    相信大家看布局文件还是看得懂的,第二个relativeLayout就是控件,我们的任务就是把这些xml写成代码(有些个别设置与xml的不同,注意属性的区别)

    我决定分三个类。第一个是WheelView,来表示这个控件,也就是说它便是第二个relativeLayout

    第二个类是CheckNumView,它表示第三个relativeLayout

    第三个类是WheelScrollView。它表示ScrollView

    显然。这个三个类的关系非常清楚,就是后一个嵌套在前一个里面

    至于其它控件。比如确定button,大家看布局文件就应该能够加上

        以下我从MainActivity開始说起。为了表示轮子,我建立了一个JAVABEAN,也就是Wheel类,这个类存储每一个轮子里面的数据。

    package com.xp.demo;
    
    /**
     * 实体类 这个类存储每一个轮子里面的数据
     * 
     * @author sooner
     * 
     */
    public class Wheel {
    	/**
    	 * 内容
    	 */
    	private String[] texts;
    
    	/**
    	 * 焦点文字
    	 */
    	private String focusText;
    
    	public Wheel(String[] texts) {
    		this.texts = texts;
    	}
    
    	public String[] getTexts() {
    		return texts;
    	}
    
    	public int getFocusTextPosition() {
    		int position = 0;
    		int count = texts.length;
    		if (count > 0) {
    			for (int i = 0; i < texts.length; i++) {
    				if (texts[i].equals(focusText)) {
    					position = i;
    				}
    			}
    			if (position == 0) {
    				position = -1;
    			}
    		} else {
    			position = -1;
    		}
    
    		return position;
    	}
    }
    
    然后是MainActivity

    package com.xp.demo;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class MainActivity extends Activity {
    	/**
    	 * 思路: 1,布局,整个控件的布局。事实上就是用代码取带xml来实现当前布局 2。能够滑动的(即滚轮)。事实上是一个ScrollView
    	 * 3,推断滑动状态的,使用: protected void onScrollChanged(int x, int y, int oldx, int
    	 * oldy) 方法
    	 */
    	WheelView wheelView;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    
    		wheelView = (WheelView) findViewById(R.id.wheelview);
    		String[] years = { "00", "01", "02", "03", "04", "05", "06", "07",
    				"08", "09", "10", "11", "12", "13", "14", "15", "16", "17",
    				"18", "19", "20", "21", "22", "23" };
    		String[] mons = { "00", "01", "02", "03", "04", "05", "06", "07", "08",
    				"09", "10", "11", "12", "13", "14", "15", "16", "17", "18",
    				"19", "20", "21", "22", "23", "24", "25", "26", "27", "28",
    				"29", "30", "31", "32", "33", "34", "35", "36", "37", "38",
    				"39", "40", "41", "42", "43", "44", "45", "46", "47", "48",
    				"49", "50", "51", "52", "53", "54", "55", "56", "57", "58",
    				"59" };
    		// 每创建一个轮子Wheel,将它添加数组,就能够动态添加轮子
    		Wheel w1 = new Wheel(years);
    		Wheel w2 = new Wheel(mons);
    		Wheel[] ws = { w1, w2 };
    		wheelView.setWheels(ws);
    
    	}
    }

    从上面的代码看出。我的初衷就是,每创建一个轮子Wheel,将它添加数组,就能够动态添加轮子

    再看main.xml

    <?

    xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <com.xp.demo.WheelView android:id="@+id/wheelview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/act_menu_bg_hover" /> </RelativeLayout>

    注意。我们刚才的test.xml仅仅是为了我思考方便的,实际上并不须要用到。真正在布局的,是在爱activity_main.xml里面添加自己定义控件

    然后是WheelView

    package com.xp.demo;
    
    import java.util.Arrays;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.Gravity;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.Toast;
    
    public class WheelView extends RelativeLayout {
    	static int rowHeight = 100;
    	Context context;
    	private CheckNumView[] numberViews;
    
    	public WheelView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		this.context = context;
    	}
    
    	/**
    	 * 获得结果
    	 * 
    	 * @return
    	 */
    	public String[] getResult() {
    		String[] nums = new String[numberViews.length];
    		for (int i = 0; i < numberViews.length; i++) {
    			nums[i] = numberViews[i].getNumber();
    		}
    		return nums;
    	}
    
    	@SuppressLint("NewApi")
    	public void setWheels(Wheel[] wheels) {
    		// 轮子数组
    		numberViews = new CheckNumView[wheels.length];
    
    		// 中间蓝色的遮蔽层
    		ImageView imageView = new ImageView(context);
    		imageView.setBackgroundResource(R.drawable.shoukuan_border4);
    		RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(
    				LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    		lp1.height = rowHeight;
    		lp1.setMargins(0, rowHeight, 0, 0);
    		imageView.setLayoutParams(lp1);
    		addView(imageView);
    
    		// 以下就是包裹滚轮的LinearLayout
    		LinearLayout llayout = new LinearLayout(context);
    		LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
    				LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    		lp2.height = rowHeight * 3;
    		llayout.setOrientation(LinearLayout.HORIZONTAL);
    		llayout.setLayoutParams(lp2);
    
    		// 将滚轮加入到LinearLayout里面
    		int i = 0;
    		for (Wheel wheel : wheels) {
    			RelativeLayout rlayout = new RelativeLayout(context);
    			LinearLayout.LayoutParams lp3 = new LinearLayout.LayoutParams(
    					LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    			lp3.width = 0;
    			lp3.weight = 4;
    			numberViews[i] = new CheckNumView(context, wheel);
    			llayout.addView(numberViews[i], lp3);
    			i++;
    		}
    
    		// 右边的确定button
    		Button btn = new Button(context);
    		btn.setText("确定");
    		btn.setTextSize(30);
    		btn.setBackgroundResource(R.drawable.btton_avtie);
    		LinearLayout.LayoutParams lp4 = new LinearLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    		lp4.gravity = Gravity.CENTER;
    		lp4.weight = 3;
    		// 点击button。弹出选中数据
    		btn.setOnClickListener(new View.OnClickListener() {
    
    			@Override
    			public void onClick(View v) {
    				Toast.makeText(context, Arrays.toString(getResult()),
    						Toast.LENGTH_SHORT).show();
    				;
    			}
    		});
    
    		llayout.addView(btn, lp4);
    
    		addView(llayout);
    	}
    
    }
        然后是CheckNumView

        事实上每一个CheckNumView就是单独一个滚轮。然而它仍然是继承RelativeLayout,而不是ScroolView,是为了更方便的调整滚轮的位置,况且,滚轮旁边另一个标志单位的TextView,显然它个滚轮(ScroolView)应该是一个总体。所以我们把ScroolView和单位TextView先包装成一个总体

    package com.xp.demo;
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.Color;
    import android.view.Gravity;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    import com.xp.demo.WheelScrollView.OnScrollStopListener;
    /**
     * 每一个CheckNumView就是单独一个滚轮,
     * 然而它仍然是继承RelativeLayout,而不是ScroolView,
     * 是为了更方便的调整滚轮的位置,况且。滚轮旁边另一个标志单位的TextView,
     * 显然它个滚轮(ScroolView)应该是一个总体,
     * 所以我们把ScroolView和单位TextView先包装成一个总体
     * @author sooner
     *
     */
    public class CheckNumView extends RelativeLayout{
    	WheelScrollView sc;
    	String[] texts;
    	private int currentY = -1000;
    	private int position = 1;
    	public CheckNumView(Context context) {
    		super(context);
    	}
    	
    	@SuppressLint("NewApi")
    	public CheckNumView(Context context, Wheel wheel) {
    		super(context);
    		//获取数据字符串数组
    		texts = wheel.getTexts();
    		
    		//这个RelativeLayout用于包裹滚轮
    		RelativeLayout rlayout = new RelativeLayout(context);
    		RelativeLayout.LayoutParams lp =  new RelativeLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);		
    		rlayout.setGravity(Gravity.CENTER);
    		
    		//单位TextView
    		TextView unit = new TextView(context);
    		unit.setText("单位");
    		unit.setId(1111);
    		unit.setGravity(Gravity.CENTER);
    		RelativeLayout.LayoutParams lp2 =  new RelativeLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    		lp2.addRule(RelativeLayout.CENTER_VERTICAL,RelativeLayout.TRUE );
    		lp2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
    		rlayout.addView(unit,lp2);
    		
    		//滚轮
    		sc = new WheelScrollView(context,texts);
    		sc.setVerticalScrollBarEnabled(false);
    		sc.setHorizontalScrollBarEnabled(false);
    		sc.setOverScrollMode(OVER_SCROLL_NEVER);
    		RelativeLayout.LayoutParams lp3=  new RelativeLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    		lp3.height = WheelView.rowHeight * 3;
    		lp3.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE );
    		lp3.addRule(RelativeLayout.LEFT_OF, 1111);				
    		
    		//这种方法,是指滚轮初始化以后的第一个位置
    		sc.post(new Runnable() {
    			
    			@Override
    			public void run() {				
    				sc.scrollTo(0, 0*WheelView.rowHeight);
    			}
    		});
    		//这种方法。设置选中位置字符串的颜色
    		setFocusText(1);
    		
    		/*
    		 * 这个是回调监听器。一旦滚轮停止滚动,就是触发
    		 * 有必要说下的是,currentY必须不断更新
    		 */
    		sc.setOnScrollStopListener(new OnScrollStopListener(){
    
    			@Override
    			public void onStop(int y) {
    				if(y != currentY) {
    					// 推断滚动误差,不到行高的一半就抹掉,超过行高的一半而不到一个行高就填满
    					if (y % WheelView.rowHeight >= (WheelView.rowHeight / 2)) {
    						y = y + WheelView.rowHeight - y % WheelView.rowHeight;
    						sc.scrollTo(0, y);
    					} else {
    						y = y - y % WheelView.rowHeight;
    						sc.scrollTo(0, y);
    					}
    					setFocusText(y / WheelView.rowHeight+1);
    				}
    				currentY = y;
    			}
    			
    		});
    		
    		rlayout.addView(sc,lp3);
    		
    		addView(rlayout,lp);																
    	}
    	/**
    	 * 设置焦点文字风格
    	 * 
    	 * @param position
    	 */
    	private void setFocusText(int position) {
    		if(this.position >= 0) {
    			sc.textViews[this.position].setTextColor(Color.BLACK);
    		}
    		sc.textViews[position].setTextColor(Color.RED);
    		this.position = position;
    	}
    
    	public String getNumber() {
    		return texts[position-1];
    	}
    }

    看到这里,假设有人被弄糊涂了。那么请记住我上面给出的第一个任务。实现布局。

    至于这里的setOnScrollStopListener方法,我们能够临时无论它,由于它与布局的实现无关

    package com.xp.demo;
    
    import android.content.Context;
    import android.os.Handler;
    import android.os.Message;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.ScrollView;
    import android.widget.TextView;
    
    public class WheelScrollView extends ScrollView implements Runnable {
    	private String[] texts;
    	public boolean isStop = false;
    	private Thread t;
    	private int y;
    	private int curY = 0;
    	public TextView[] textViews;
    	/*
    	 * 使用handler是为了改动主线程ui,也就是CheckNumView里面的setFocusText()方法
    	 * 假设不须要改变ui,我大可不必使用handler。直接用一个子线程来通知listener就能够了
    	 */
    	private Handler handler = new Handler() {
    
    		@Override
    		public void handleMessage(Message msg) {
    			if (isStop) {
    				listener.onStop(curY);
    				isStop = false;
    			}
    			y = -100;
    			curY = 0;
    		}
    
    	};
    
    	// 监听器
    	private OnScrollStopListener listener;
    
    	public WheelScrollView(Context context, String[] texts) {
    		super(context);
    		this.texts = texts;
    		// scrollview里面的textViews
    		textViews = new TextView[texts.length + 2];
    
    		// scrollview里面LinearLayout
    		LinearLayout llayout = new LinearLayout(context);
    		RelativeLayout.LayoutParams lp4 = new RelativeLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    		lp4.height = WheelView.rowHeight * 3;
    		lp4.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
    		llayout.setOrientation(LinearLayout.VERTICAL);
    
    		/*
    		 * 以下将textViews逐一加到LinearLayout里面
    		 * 而且设置头一个空白的textViews。跟尾一个空白的textViews,这种目的是由于我们选中的项是在中间
    		 */
    		RelativeLayout.LayoutParams lp5 = new RelativeLayout.LayoutParams(
    				LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
    		lp5.height = WheelView.rowHeight;
    		textViews[0] = new TextView(context);
    		textViews[0].setGravity(Gravity.CENTER_VERTICAL);
    		textViews[0].setText("");
    		llayout.addView(textViews[0], lp5);
    		int i = 1;
    		for (String text : texts) {
    			textViews[i] = new TextView(context);
    			textViews[i].setGravity(Gravity.CENTER_VERTICAL);
    			textViews[i].setText(text);
    			llayout.addView(textViews[i], lp5);
    			i++;
    		}
    		textViews[i] = new TextView(context);
    		textViews[i].setGravity(Gravity.CENTER_VERTICAL);
    		textViews[i].setText("");
    		llayout.addView(textViews[i], lp5);
    
    		// 将LinearLayout增加ScrollView
    		addView(llayout, lp4);
    	}
    
    	// 滚动时自己主动调用该函数,获取y值
    	@Override
    	protected void onScrollChanged(int x, int y, int oldx, int oldy) {
    		super.onScrollChanged(x, y, oldx, oldy);
    		this.y = y < 0 ? 0 : y;
    	}
    
    	// 回调接口
    	public static interface OnScrollStopListener {
    		public void onStop(int y);
    	}
    
    	public void setOnScrollStopListener(OnScrollStopListener listener) {
    		this.listener = listener;
    	}
    
    	// 降低滚动的速度
    	@Override
    	public void fling(int velocityY) {
    		super.fling(velocityY / 3);
    	}
    
    	// 这里是推断滚动触发開始,与滚动触发停止的
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		if (ev.getAction() == MotionEvent.ACTION_UP) {
    			isStop = true;
    			if (t == null) {
    				t = new Thread(this);
    				t.start();
    			} else if (!t.isAlive()) {
    				t = new Thread(this);
    				t.start();
    			}
    		} else if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    			isStop = false;
    		}
    		return super.onTouchEvent(ev);
    	}
    
    	// 假设通知滚动。新线程将使用handler请求改动ui。而且调用回调函数,式选项在正确的位置上
    	@Override
    	public void run() {
    		while (isStop) {
    			try {
    				if (curY == y) {
    					handler.sendEmptyMessage(0);
    				} else {
    					curY = y;
    				}
    				Thread.sleep(60);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    相同,假设这都代码看不懂,你能够先忽略一些与布局无关的东西(除了构造函数,基本其它函数都与布局无关)。

    忽略这些代码以后。我相信已经能够画出这个控件,而且能够拖动了

    以下的问题就是我们希望拖到两个选项中间,脱手时,会自己主动对准某一个近期的选项

    这是我们就须要用到其它的代码了。

    思路是使用onTouchEvent(MotionEvent ev)来推断滑动開始与结束

    一点滑动结束,我们就要拿到当前的y值。然后通过一个线程,调用handler去通知CheckNumView里面的OnScrollStopListener,最后我们在onstop()函数里面。处理这个y值

    一个疑问是为什么获得y值以后,要通过线程调用handler。理由是防止再次TouchEvent影响前一次TouchEvent的结果

    第二个疑问是,为什么要记录curY,由于仅仅有curY==y,我们才干确定滑动停止了

    OK,几个由于攻克了,相信大家看着我的代码。应该豁然开朗了。

    參考博客:http://blog.csdn.net/crazy__chen/article/details/41958677

    參考源代码:http://download.csdn.net/detail/u010963246/8922921

    源代码下载:http://download.csdn.net/detail/u010963246/8922911


  • 相关阅读:
    kettle-学习参考
    spring retry 重试机制完整例子
    一个四五年的Java开发程序员,该准备哪些去面试?
    Java之io nio aio 的区别
    Java对象的存活判断
    Java+微信支付(下预购单+回调+退款+查询账单)
    Java +支付宝 +接入
    关于MQ 消息队列的通俗理解和 rabbitMQ 使用
    java 对接芝麻信用 -用芝麻私钥解密错误
    mysql 乐观判断 校验
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7101313.html
Copyright © 2011-2022 走看看