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


  • 相关阅读:
    uva 10369 Arctic Network
    uvalive 5834 Genghis Khan The Conqueror
    uvalive 4848 Tour Belt
    uvalive 4960 Sensor Network
    codeforces 798c Mike And Gcd Problem
    codeforces 796c Bank Hacking
    codeforces 768c Jon Snow And His Favourite Number
    hdu 1114 Piggy-Bank
    poj 1276 Cash Machine
    bzoj 2423 最长公共子序列
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7101313.html
Copyright © 2011-2022 走看看