Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android滾動選取金額

Android滾動選取金額

編輯:關於Android編程

UI效果圖:

\

最終的效果是可以滑動刻度來選取金額,並且滑動停止後必須定位到某個金額上,不能停留在中間。

分析:決定用listview來實現上述效果

分析UI圖,發現有三種類型的item,短的,長的,還有長的帶文字的。

1.listview所用的adapter的實現。

 

ListAdaptera.java文件

 

package com.miduo.financialmanageclient.ui.adapter;

import java.util.List;

import android.content.Context;
import android.content.ClipData.Item;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.miduo.financialmanageclient.R;

/**
 * 立即投資頁面刻度 本來覺得不用復用了,結果發現會卡死,還是得復用
 * 
 * @author huozhenpeng
 * 
 */
public class ListAdaptera extends BaseAdapter {

	private Context context;
	private List lists;
	private static final int TYPE_ITEM_FIRST = 0;
	private static final int TYPE_ITEM_SECOND = 1;
	private static final int TYPE_ITEM_THREE = 2;

	public ListAdaptera(Context context, List lists) {
		this.context = context;
		this.lists = lists;
	}

	@Override
	public int getCount() {
		return lists.size();
	}

	@Override
	public Object getItem(int position) {
		return lists.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public int getItemViewType(int position) {
		if (position == 0 || position % 10 == 0) {
			return TYPE_ITEM_FIRST;
		} else if (position % 5 == 0) {
			return TYPE_ITEM_SECOND;
		} else {
			return TYPE_ITEM_THREE;
		}
	}

	@Override
	public int getViewTypeCount() {
		return 3;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		int type = getItemViewType(position);
		if (convertView == null) {
			switch (type) {
			case TYPE_ITEM_FIRST:
				viewHolder = new ViewHolder();
				convertView = LayoutInflater.from(context).inflate(
						R.layout.item_list, null);
				viewHolder.tv_left = ((TextView) convertView
						.findViewById(R.id.tv_left));
				viewHolder.tv_right = ((TextView) convertView
						.findViewById(R.id.tv_right));
				convertView.setTag(viewHolder);
				break;
			case TYPE_ITEM_SECOND:
				convertView = LayoutInflater.from(context).inflate(
						R.layout.item_list3, null);
				break;
			case TYPE_ITEM_THREE:
				convertView = LayoutInflater.from(context).inflate(
						R.layout.item_list2, null);
				break;
			default:
				break;
			}
		}
		switch (type) {
		case TYPE_ITEM_FIRST:
			viewHolder = (ViewHolder) convertView.getTag();
			viewHolder.tv_left.setText(lists.get(position) + "萬");
			viewHolder.tv_right.setText(lists.get(position) + "萬");
			break;
		case TYPE_ITEM_SECOND:
			break;
		case TYPE_ITEM_THREE:
			break;
		default:
			break;
		}

		return convertView;
	}

	final static class ViewHolder {
		TextView tv_left, tv_right;
	}

}

三個布局文件:

 

item_list.xml文件

 



    
    

    
    



 

item_list2.xml文件

 



    

    


item_list3.xml文件

 

 



    

    



 

注意:1.剛剛開始覺得布局文件比較簡單,沒有必要復用,後來發現如果計算出來的刻度特別多滑動又比較快會卡死。

2.適配方式采用等比例適配。(就是說在720的手機上大小是72px的換到1080的手機上則占108px)。

先粘貼上全部代碼,再對代碼進行分析

MainActivityt.java類

 

package com.example.wavedemo;

import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivityt extends Activity {

	private ListView listview;
	private List lists = new ArrayList();
	private ListAdaptera adapter;
	private int position;
	private int top;
	private int itemHeight;
	private int height;
	private int deltaItemNum;// 差距條數
	private int remainder;// 余數
	// 全部以萬為單位
	private int startMoney = 5;// 起投金額
	private int deltaMoney = 1;// 遞增金額
	private int canInvestMoney = 1097;// 可投金額
	// 補一個頭部
	private LinearLayout ll_head;
	// 補一個footer
	private LinearLayout ll_footer;
	// 靜止之後實際的position
	private int actualPosition;

	@TargetApi(19)
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_maint);
		itemHeight = (int) getResources().getDimension(R.dimen.px2dp_26);
		height = (int) getResources().getDimension(R.dimen.px2dp_544);
		initHead();
		initFooter();
		// 算出總共有多少個實際的格子(可以滑動到中間位置上的)
		for (int i = startMoney; i <= canInvestMoney; i += deltaMoney) {
			lists.add(i);
		}
		adapter = new ListAdaptera(this, lists);
		listview = (ListView) this.findViewById(R.id.listview);
		listview.addHeaderView(ll_head);
		listview.addFooterView(ll_footer);
		listview.setAdapter(adapter);
		listview.setOnItemSelectedListener(new OnItemSelectedListener() {

			@Override
			public void onItemSelected(AdapterView parent, View view,
					int position, long id) {

			}

			@Override
			public void onNothingSelected(AdapterView parent) {

			}
		});
		listview.setOnScrollListener(new OnScrollListener() {

			@SuppressLint("NewApi")
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				switch (scrollState) {
				case SCROLL_STATE_FLING:// 手指離開屏幕後,慣性滑動

					break;
				case SCROLL_STATE_IDLE:// 滑動後靜止
					position = listview.getFirstVisiblePosition();// 第幾個item
					top = getViewByPosition(position, listview).getTop();
					if (position == 0) {
						if (top == 0 || -top <= itemHeight / 2)// 定位到起投金額
						{
							listview.setSelectionFromTop(1,
									(height - itemHeight) / 2);
							actualPosition = 0;
						} else {
							listview.setSelectionFromTop(
									-(top + itemHeight / 2) / itemHeight + 2,
									(height - itemHeight) / 2);
							actualPosition = -(top + itemHeight / 2)
									/ itemHeight + 1;
						}
					} else {
						deltaItemNum = (height / 2 - (itemHeight + top))
								/ itemHeight;
						listview.setSelectionFromTop(position + deltaItemNum
								+ 1, (height - itemHeight) / 2);
						actualPosition = position + deltaItemNum;
					}
					MToast.showToast(MainActivityt.this,
							lists.get(actualPosition) + "萬");
					showHighLight(actualPosition, listview);
					break;
				case SCROLL_STATE_TOUCH_SCROLL:// 手指在屏幕上滑動
					break;

				default:
					break;
				}

			}

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {

			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		return true;
	}

	public View getViewByPosition(int pos, ListView listView) {
		final int firstListItemPosition = listView.getFirstVisiblePosition();
		final int lastListItemPosition = firstListItemPosition
				+ listView.getChildCount() - 1;

		if (pos < firstListItemPosition || pos > lastListItemPosition) {
			return listView.getAdapter().getView(pos, null, listView);
		} else {
			final int childIndex = pos - firstListItemPosition;
			return listView.getChildAt(childIndex);
		}
	}

	/**
	 * 添加輔助頭部
	 */
	private void initHead() {
		ll_head = new LinearLayout(this);
		ll_head.setOrientation(LinearLayout.VERTICAL);
		AbsListView.LayoutParams params = new AbsListView.LayoutParams(
				AbsListView.LayoutParams.MATCH_PARENT,
				(height - itemHeight) / 2);
		ll_head.setLayoutParams(params);
		ll_head.addView(new View(this), 0, new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT, itemHeight / 2));
		int total = (height - itemHeight) / 2 / itemHeight + 1;
		View view = null;
		for (int i = 1; i <= total; i++) {
			if (i % 5 == 0) {
				view = LayoutInflater.from(this).inflate(R.layout.item_list3,
						null);
			} else {
				view = LayoutInflater.from(this).inflate(R.layout.item_list2,
						null);
			}
			ll_head.addView(view, 0);
		}

	}

	/**
	 * 添加輔助頭部
	 */
	private void initFooter() {
		ll_footer = new LinearLayout(this);
		ll_footer.setOrientation(LinearLayout.VERTICAL);
		AbsListView.LayoutParams params = new AbsListView.LayoutParams(
				AbsListView.LayoutParams.MATCH_PARENT,
				(height - itemHeight) / 2);
		ll_footer.setLayoutParams(params);
		ll_footer.addView(new View(this), 0, new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT, itemHeight / 2));
		int total = (height - itemHeight) / 2 / itemHeight + 1;
		View view = null;
		for (int i = 1; i <= total; i++) {

			view = LayoutInflater.from(this).inflate(R.layout.item_list2, null);
			ll_footer.addView(view, 0);
		}

	}

	private void showHighLight(int pos, ListView listview) {
		View view = getViewByPosition(pos + 1, listview);
		TextView tv_left = (TextView) view.findViewById(R.id.tv_left);
		TextView tv_right = (TextView) view.findViewById(R.id.tv_right);
		if (tv_left != null) {
			tv_left.setTextColor(Color.parseColor("#fe7800"));
			tv_right.setTextColor(Color.parseColor("#fe7800"));
		}
	}

}

AndroidManifest.xml文件

 

 




    

    
        
            
                

                
            
        
        
    



運行效果:

 

\

代碼分析:

 

定義listview每個item的高度

itemHeight = (int) getResources().getDimension(R.dimen.px2dp_26);
定義listview的總高度
height = (int) getResources().getDimension(R.dimen.px2dp_544);
初始化listview頭部
initHead();
初始化listview腳部
initFooter();
為什麼要給listview添加頭部和腳部呢,

 

首先在剛剛進入頁面的時候必須保證起投金額位於位於listview的正中間,此時listview可以向下滑動但是不能向上滑動,所以為了保證起投金額(5萬,是adapter數據源(集合)中的第一個數據)位於listview正中間,需要給listview添加個頭部,所以5萬以上展現出來的效果其實是listview的一個頭部。

代碼中:

/**
	 * 添加輔助頭部
	 */
	private void initHead() {
		ll_head = new LinearLayout(this);
		ll_head.setOrientation(LinearLayout.VERTICAL);
		AbsListView.LayoutParams params = new AbsListView.LayoutParams(
				AbsListView.LayoutParams.MATCH_PARENT,
				(height - itemHeight) / 2);
		ll_head.setLayoutParams(params);
		ll_head.addView(new View(this), 0, new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT, itemHeight / 2));
		int total = (height - itemHeight) / 2 / itemHeight + 1;
		View view = null;
		for (int i = 1; i <= total; i++) {
			if (i % 5 == 0) {
				view = LayoutInflater.from(this).inflate(R.layout.item_list3,
						null);
			} else {
				view = LayoutInflater.from(this).inflate(R.layout.item_list2,
						null);
			}
			ll_head.addView(view, 0);
		}

	}

頭部的計算稍微復雜一些,因為為了美觀,5萬的item是一個長的帶刻度的,向上必須是四個小刻度再往上就是一個長的不帶刻度的。

 

首先需要知道一個item的范圍是多少:

\

 

 

青色框框住的部分是每個item的范圍。相當於是每個刻度線在整個item內是居中顯示的。

所以在計算頭部高度的時候

 

AbsListView.LayoutParams params = new AbsListView.LayoutParams(
				AbsListView.LayoutParams.MATCH_PARENT,
				(height - itemHeight) / 2);

 

 

 

這裡首先添加了半個item的高度,這半個item相當於是5萬的那半個

 

ll_head.addView(new View(this), 0, new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT, itemHeight / 2));

 

然後計算出剩余部分所需的item條數

int total = (height - itemHeight) / 2 / itemHeight + 1;

 

由於整形計算會捨棄精度,所以最後加上1

 

 

添加listview腳部也是相同的原理,為了能使最大額度(1097)滑到listview中間,所以需要加入腳部,腳部的代碼比較簡單,不做分析。

 

核心代碼:

 

 

case SCROLL_STATE_IDLE:// 滑動後靜止
					position = listview.getFirstVisiblePosition();// 第幾個item
					top = getViewByPosition(position, listview).getTop();
					if (position == 0) {
						if (top == 0 || -top <= itemHeight / 2)// 定位到起投金額
						{
							listview.setSelectionFromTop(1,
									(height - itemHeight) / 2);
							actualPosition = 0;
						} else {
							listview.setSelectionFromTop(
									-(top + itemHeight / 2) / itemHeight + 2,
									(height - itemHeight) / 2);
							actualPosition = -(top + itemHeight / 2)
									/ itemHeight + 1;
						}
					} else {
						deltaItemNum = (height / 2 - (itemHeight + top))
								/ itemHeight;
						listview.setSelectionFromTop(position + deltaItemNum
								+ 1, (height - itemHeight) / 2);
						actualPosition = position + deltaItemNum;
					}
					MToast.showToast(MainActivityt.this,
							lists.get(actualPosition) + "萬");
					showHighLight(actualPosition, listview);
					break;


 

position = listview.getFirstVisiblePosition();// 第幾個item
這條代碼比較簡單,就是第一個可見的item,從0到很大

 

 

top = getViewByPosition(position, listview).getTop();
得到每個item的top值(Top position of this view relative to its parent.)

 

 

然後判斷position是不是等於0,等於0說明起投金額(5萬)還沒有劃出屏幕,這段代碼的意思是應該滑動靜止後應該定位到起投金額,

actualPosition是完全靜止後實際的position,記錄下來從集合中取數據用。

top==0是初始狀態下,向下拖拽listview的情況

 

-top <= itemHeight / 2是向上拽,但是距離小於item一半的高度

 

setSelectionFromTop(int position,int y)

 

@param position Index (starting at 0) of the data item to be selected.

@param y The distance from the top edge of the ListView (plus padding) that the

item will be positioned.

也就是說position是要被選中的item,從0開始,y是距離被選中的item的高度,代碼中寫的1,是因為listview加了頭部,頭部算0位置。
(height - itemHeight) / 2)
是listview頭部的高度,就是說第一個item距離頂部的距離為

(height - itemHeight) / 2


 

if (top == 0 || -top <= itemHeight / 2)// 定位到起投金額
						{
							listview.setSelectionFromTop(1,
									(height - itemHeight) / 2);
							actualPosition = 0;
						}

 

 

在向下滑動時,top一直為負值,並不斷減小(-1,-2,-3,................)

 

-(top + itemHeight / 2)

這裡的itemHeight/2是起投哪個item的下半截高度

 

 else {
							listview.setSelectionFromTop(
									-(top + itemHeight / 2) / itemHeight + 2,
									(height - itemHeight) / 2);
							actualPosition = -(top + itemHeight / 2)
									/ itemHeight + 1;
						}

 

 

---------------------------------分割線-------------------------------------------------分割線--------------------------------------------------分割線------------------------

分析另一種情況

這種情況下的top的絕對值肯定會小於itemHeight的值

 

else {
						deltaItemNum = (height / 2 - (itemHeight + top))
								/ itemHeight;
						listview.setSelectionFromTop(position + deltaItemNum
								+ 1, (height - itemHeight) / 2);
						actualPosition = position + deltaItemNum;
					}

這種情況是第一個可見的item不在是第一個item(不是起投的那個item)
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved