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<Integer> 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<Integer> 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文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <View
        android:id="@+id/view1"
        android:layout_centerVertical="true"
        android:background="#999999"
        android:layout_width="@dimen/px2dp_28"
        android:layout_height="@dimen/px2dp_2" />
    <TextView
        android:id="@+id/tv_left"
        android:layout_toRightOf="@id/view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:text="10万"
        android:textColor="#999999"
        android:textSize="@dimen/px2sp_20"
        android:layout_marginLeft="@dimen/px2dp_6"
        />

    <View
        android:id="@+id/view2"
        android:layout_centerVertical="true"
        android:background="#999999"
        android:layout_width="@dimen/px2dp_28"
        android:layout_height="@dimen/px2dp_2"
        android:layout_alignParentRight="true" />
    <TextView
        android:id="@+id/tv_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:text="10万"
        android:textColor="#999999"
        android:textSize="@dimen/px2sp_20"
        android:layout_toLeftOf="@id/view2"
        android:layout_marginRight="@dimen/px2dp_6"
        />

</RelativeLayout>

item_list2.xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:minHeight="@dimen/px2dp_26"
    android:layout_height="@dimen/px2dp_26" >

    <View
        android:layout_centerVertical="true"
        android:background="#999999"
        android:layout_width="@dimen/px2dp_18"
        android:layout_height="@dimen/px2dp_2" />

    <View
        android:layout_centerVertical="true"
        android:background="#999999"
        android:layout_width="@dimen/px2dp_18"
        android:layout_height="@dimen/px2dp_2"
        android:layout_alignParentRight="true" />

</RelativeLayout>

item_list3.xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:minHeight="@dimen/px2dp_26"
    android:layout_height="@dimen/px2dp_26" >

    <View
        android:id="@+id/view1"
        android:layout_width="@dimen/px2dp_28"
        android:layout_height="@dimen/px2dp_2"
        android:layout_centerVertical="true"
        android:background="#999999" />

    <View
        android:id="@+id/view2"
        android:layout_width="@dimen/px2dp_28"
        android:layout_height="@dimen/px2dp_2"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:background="#999999" />

</RelativeLayout>

注意: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<Integer> lists = new ArrayList<Integer>();
	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文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.wavedemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:hardwareAccelerated="false"
            android:name="com.example.wavedemo.MainActivityt"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.example.wavedemo.LoginActivity"></activity>
    </application>

</manifest>

运行效果:

代码分析:

定义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)

时间: 2024-10-31 10:27:22

Android滚动选取金额的相关文章

用RollViewPager实现Android滚动banner

最近项目中要实现一个滚动banner,效果大体如下图 这个自己写实在是不方便,而且写出来也很难保证没有bug和性能缺陷,好在网上有人开源了一个实现滚动banner的RollViewPager框架,亲测方便可行.这篇博文简单记录一下如何使用开源框架RollViewPager实现Android滚动banner,想要深入学习可以去github下载源码:https://github.com/Jude95/RollViewPager 这里主要实现的效果有:1. 图片无限轮播 2. 触摸时暂停轮播 3. 指

android 滚动栏下拉反弹的效果(相似微信朋友圈)

微信朋友圈上面的图片封面,QQ空间说说上面的图片封面都有下拉反弹的效果,这些都是使用滚动栏实现的.下拉,当松开时候.反弹至原来的位置.下拉时候能看到背景图片.那么这里简介一下这样的效果的实现. 本文源代码下载:点击 1.效果图 这部手机显示的分辨率有限,非常老的手机调试. 2.具有反弹效果BounceScrollView package com.org.scroll; import android.content.Context; import android.graphics.Rect; im

Android 图像选取 图片剪裁 照相选图 照相裁剪 大全 6-19更新

前言 已经完整打包成一个工具 , 添加了图像压缩和修改了图像剪裁功能 , 项目地址在这里 https://github.com/ocwvar/PicturePicker 本篇讲的是使用 "Intent.ACTION_PICK" 来选取图片并进行剪裁加载的操作 , 包括以下两个功能 从本地相册读取图片进行剪裁 从照相机获取图片进行剪裁 注意: 本篇使用一个工具类PickUriUtils 使Uri转换成文件路径 , 工具类在文章最后给出. 本文的Bitmap对象没有进行回收和缓存 , 在真

android 滚动时间选择器

一.概述 滚动时间选择现在貌似很常用,所以就总结一下,显示效果一般般 , 做个参考吧! 以上就是效果图,可以滚动选择 日期时间, 由于是在 5.0系统运行的,貌似5.0系统做了什么变动,下面的 "取消","确定" 默认不会居中显示,我也不知道怎么才能让它居中显示, 但是在5.0以下系统运行 默认是居中显示的. 二.布局文件 <?xml version="1.0" encoding="utf-8"?> <Lin

android开发步步为营之66:android图片选取

最近做一个页面,反馈问题页面,有个用户上传问题图片的功能.本来很笨的想把系统的所有图片列出来,然后让用户选择,后来发现原来可以直接打开手机所有图片的api的.效果如图: 给出主要代码: 1.选择图片 Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, 1); 2.获取图片 protect

android滚动图片

关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户"友好性",下面来看几个示例图:     再来看下我仿写的效果: 关于广告轮播Banner这个东西,GitHub上面应该有现成的开源组件,不过我没去找过,觉得实现起来不会太难,就自己去仿写了,下面我说下实现的思路: 1.首先看到这个可以滑动切换图片的界面,我们很自然就会想到ViewPager

Android 滚动RecyclerView加载图片时的流畅度优化

实现:使用onScrollStateChanged回调检测滚动状态,并在RecyclerViewAdapter内部设置类似isScrolling的状态值来控制网络图片的加载. 下面是代码举例: // BaseAdapter中添加如下代码 public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { …… protected boolea

Android 滚动视图(ScollView)

1.介绍 2.使用技巧 3.xml文件代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="ht

Android EditText 输入金额(小数点后两位)

EditText mEdit = new EditText(context); InputType.TYPE_NUMBER_FLAG_DECIMAL.小数点型 InputType.TYPE_CLASS_NUMBER 整数型 设置Input的类型两种都要 mEdit.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL|InputType.TYPE_CLASS_NUMBER); 设置字符过滤 mEdit.setFilters(new InputFilter