-
先给出ExpandableListView的一些效果图
先给出ExpandableListViewAdapter的代码:
package com.bit.expandablelistview; import java.util.HashMap; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.TextView; public class ExpandableListViewAdapter extends BaseExpandableListAdapter { private List group;//这是组视图 private HashMap> child;//这是子视图 private ExpandableListView listView;//当选择另一个组视图时关闭当前打开的组视图 private LayoutInflater inflater;//加载布局文件 private int last;//判断是否打开当前的页面 public ExpandableListViewAdapter(Context context, List group, HashMap> child, ExpandableListView listView) { this.group = group; this.child = child; this.listView = listView; this.inflater = LayoutInflater.from(context); } /** * 对外提供一个借口,当数据改变时调用这个函数 * @param group * @param child */ public void setOnDataChanged(List group, HashMap> child) { this.group = group; this.child = child; this.notifyDataSetChanged(); } @Override public int getGroupCount() { // TODO Auto-generated method stub return group.size(); } @Override public int getChildrenCount(int groupPosition) { List tempList = child.get(group.get(groupPosition)); if (tempList != null) { return tempList.size(); } else { return 0; } } @Override public Object getGroup(int groupPosition) { // TODO Auto-generated method stub return group.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { // TODO Auto-generated method stub return child.get(group.get(groupPosition)).get(childPosition); } @Override public long getGroupId(int groupPosition) { // TODO Auto-generated method stub return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { // TODO Auto-generated method stub return childPosition; } @Override public boolean hasStableIds() { // TODO Auto-generated method stub return false; } /** * 得到组视图布局文件,对这个进行了优化,可以看到converView是一个Android提供的缓存的对象,如果第一次没有这个布局文件 * 就创建一个,如果有的话,直接从convertView得到,不用进行创建,减少内存开销 */ public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { String groupName = group.get(groupPosition); View view; TextView name; if (convertView == null) { view = inflater.inflate(R.layout.group_item, null); name = (TextView) view.findViewById(R.id.group_number); view.setTag(name); } else { view = convertView; name = (TextView) view.getTag(); } name.setText(groupName); return view; } private class ViewHolder { ImageView picture; TextView name; } /** * 得到子视图的布局文件,类似于组视图 */ @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { User user = child.get(group.get(groupPosition)).get(childPosition); View view; ViewHolder viewHolder; if (convertView == null) { view = inflater.inflate(R.layout.child_item, null); viewHolder = new ViewHolder(); viewHolder.picture = (ImageView) view.findViewById(R.id.pic); viewHolder.name = (TextView) view.findViewById(R.id.child_name); view.setTag(viewHolder); } else { view = convertView; viewHolder = (ViewHolder) view.getTag(); } viewHolder.name.setText(user.getName()); viewHolder.picture.setImageResource(user.getPicture()); return view; } /** * 设置可以点击子视图 */ @Override public boolean isChildSelectable(int groupPosition, int childPosition) { // TODO Auto-generated method stub return true; } @Override public void onGroupExpanded(int groupPosition) { super.onGroupExpanded(groupPosition); if (groupPosition != last) { listView.collapseGroup(last); } last = groupPosition; } }
然后给出MainActivity类的代码package com.bit.expandablelistview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.ExpandableListView; import android.widget.ExpandableListView.OnChildClickListener; import android.widget.Toast; public class MainActivity extends Activity implements OnChildClickListener { private List group;//这是组视图 private HashMap> child;//这是子视图 private ExpandableListView listView;//这是可以扩展的listview private ExpandableListViewAdapter adapter; //这是子视图要加载的所有图片 private int pic[] = new int[] { R.drawable.head1, R.drawable.head2, R.drawable.head3, R.drawable.head4, R.drawable.head5, R.drawable.head6, R.drawable.head7, R.drawable.head8, R.drawable.head9, R.drawable.head10 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); prepareData(); showData(); } /** * 显示数据 */ private void showData() { if (adapter == null) { adapter = new ExpandableListViewAdapter(this, group, child, listView); listView.setAdapter(adapter); } else { adapter.setOnDataChanged(group, child); } } /** * 初始化一些参数,设置监听事件 */ private void init() { group = new ArrayList(); child = new HashMap>(); listView = (ExpandableListView) findViewById(R.id.expandlistview); listView.setOnChildClickListener(this); } /** * 准备测试数据 */ private void prepareData() { group.add(第一组); group.add(第二组); group.add(第三组); List users = new ArrayList(); for (int i = 0; i < 4; i++) { User user = new User(); user.setName(张全蛋 + i); user.setPicture(pic[i]); users.add(user); } child.put(group.get(0), users); users = new ArrayList(); for (int i = 4; i < 7; i++) { User user = new User(); user.setName(李铁柱 + i); user.setPicture(pic[i]); users.add(user); } child.put(group.get(1), users); users = new ArrayList(); for (int i = 7; i < 10; i++) { User user = new User(); user.setName(唐马儒 + i); user.setPicture(pic[i]); users.add(user); } child.put(group.get(2), users); } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { //点击显示当前子视图的位置 Toast.makeText( MainActivity.this, 你点击的是 + child.get(group.get(groupPosition)) .get(childPosition).getName(), Toast.LENGTH_SHORT).show(); return false; } }
大家注意,其中的User类是自己定义的一个类。首先,这是两张今天要实现的效果图:
最主要的实现自己写的一个HistogramView的类(柱状图类)
HistogramView的代码如下:
package com.example.histogram.widet; import com.example.changepage1.R; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.view.animation.Animation; import android.view.animation.Transformation; /** * 这是重新写的一个柱状图的view * Author: 李垭超 email:296777513@qq.com * Date: 2015-1-3 * Time: 下午6:15 */ public class HistogramView extends View { private boolean Text = false;//判断是否在柱状图上显示数字 private int Height;//控件高度 private int Width;//控件宽度 private Bitmap bitmap;//柱状图的样子 private int mHeight;//柱状图高度 private int AnimValue;//实现的动画 private double Progress; private Canvas canvas;//画出柱状图的各个属性 private HistogramAnimation ani; public void setText(boolean mText) { this.Text = mText; invalidate(); } public HistogramView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); ani = new HistogramAnimation();//初始化自定义的动画类 ani.setDuration(1000);//设置整个动画在1秒内完成 } public HistogramView(Context context, AttributeSet attrs) { super(context, attrs); ani = new HistogramAnimation(); ani.setDuration(1000); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //初始化控件和进度的条相关参数 Width = w; Height = h; mHeight = (int) (h * Progress * 0.9); } @SuppressLint(DrawAllocation) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); this.canvas = canvas; Paint paint = new Paint();//设置矩形画笔,设置柱状图的信息 paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); paint.setTextSize(sp2px(getContext(),12)); paint.setColor(Color.parseColor(#6DCAEC)); // 绘制 柱状图的形状 :left,top,right,bottom RectF dst = new RectF(0, Height - AnimValue, Width, Height); //取出图片,并且转换为bitmap类型 bitmap = BitmapFactory .decodeResource(getResources(), R.drawable.column); this.canvas.drawBitmap(bitmap, null, dst, paint);//画出柱状图 if (Text) { if (Progress != 0) { this.canvas.drawText((int) (Progress * 10000) + , 0, (Height - AnimValue) - 10, paint); } else { this.canvas.drawText((int) (Progress * 10000) + , 0, (Height - AnimValue) - 10, paint); } } } /** * 对外提供接口来传进数值 * @param Progress */ public void setProgress(double Progress) { if (Progress < 0.03 && Progress != 0) { this.Progress = Progress; Progress = 0.03; } this.Progress = Progress; this.startAnimation(ani); } /** * 集成animation的一个动画类 * @author 李垭超 * */ private class HistogramAnimation extends Animation { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if (interpolatedTime < 1.0f) { AnimValue = (int) (mHeight * interpolatedTime); } else { AnimValue = mHeight; } postInvalidate(); } } public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
重写FragmentAnalysis类package com.example.histogram; import java.text.SimpleDateFormat; import java.util.Calendar; import com.example.changepage1.R; import com.example.histogram.widet.HistogramView; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.annotation.SuppressLint; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.Transformation; import android.widget.TextView; /** * 这是分析七天步数的碎片 Author: 李垭超 email:296777513@qq.com Date: 2015-1-2 Time: 下午2:39 */ public class FragmentAnalysis extends Fragment implements OnClickListener { private HistogramView hv1, hv2, hv3, hv4, hv5, hv6, hv7;// 这是7个条形柱状图 private TextView day1, day2, day3, day4, day5, day6, day7;// 这是底部显示的一周7天 private TextView average_step;// 平均步数 private TextView sum_step;// 总共步数 private int average = 0; private int sum = 0; private int average1 = 0; private int sum1 = 0; private Calendar calendar;// 日期的操作 private String day; private View view; private AllAnimation ani;// 设置的动画 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.analysis, container, false); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); init(); setWeek(); setProgress(); view.startAnimation(ani); } /** * 初始化一些对象 */ private void init() { average_step = (TextView) view.findViewById(R.id.average_step); sum_step = (TextView) view.findViewById(R.id.sum_step); ani = new AllAnimation();// 创建自定义的动画对象 ani.setDuration(1000);// 设置完成动画的时间为1秒 calendar = Calendar.getInstance();// 对日期进行实例化,显示当天的日期 hv1 = (HistogramView) view.findViewById(R.id.map1); hv2 = (HistogramView) view.findViewById(R.id.map2); hv3 = (HistogramView) view.findViewById(R.id.map3); hv4 = (HistogramView) view.findViewById(R.id.map4); hv5 = (HistogramView) view.findViewById(R.id.map5); hv6 = (HistogramView) view.findViewById(R.id.map6); hv7 = (HistogramView) view.findViewById(R.id.map7); // 对7个柱状图设置点击时间,可以显示具体的数值 hv1.setOnClickListener(this); hv2.setOnClickListener(this); hv3.setOnClickListener(this); hv4.setOnClickListener(this); hv5.setOnClickListener(this); hv6.setOnClickListener(this); hv7.setOnClickListener(this); // 显示对应的周数 day1 = (TextView) view.findViewById(R.id.Monday); day2 = (TextView) view.findViewById(R.id.Tuesday); day3 = (TextView) view.findViewById(R.id.Wednesday); day4 = (TextView) view.findViewById(R.id.Thursday); day5 = (TextView) view.findViewById(R.id.Friday); day6 = (TextView) view.findViewById(R.id.Saturday); day7 = (TextView) view.findViewById(R.id.Sunday); } @SuppressLint(SimpleDateFormat) private void setProgress() { SimpleDateFormat sdf = new SimpleDateFormat(yyyyMMdd); day = sdf.format(calendar.getTime()); // Toast.makeText(getActivity(), day + , Toast.LENGTH_LONG).show(); hv1.setProgress((5000 / 10000.0)); sum += 5000; calendar.add(Calendar.DAY_OF_MONTH, -1);//把日期设置成为 day = sdf.format(calendar.getTime()); hv2.setProgress((3000 / 10000.0)); sum += 3000; // Toast.makeText(getActivity(), day+, Toast.LENGTH_LONG).show(); calendar.add(Calendar.DAY_OF_MONTH, -1); day = sdf.format(calendar.getTime()); hv3.setProgress((2000 / 10000.0)); sum += 2000; calendar.add(Calendar.DAY_OF_MONTH, -1); day = sdf.format(calendar.getTime()); hv4.setProgress((7631 / 10000.0)); sum += 7631; calendar.add(Calendar.DAY_OF_MONTH, -1); day = sdf.format(calendar.getTime()); hv5.setProgress((4213 / 10000.0)); sum += 4213; calendar.add(Calendar.DAY_OF_MONTH, -1); day = sdf.format(calendar.getTime()); hv6.setProgress((8431/ 10000.0)); sum += 8431; calendar.add(Calendar.DAY_OF_MONTH, -1); day = sdf.format(calendar.getTime()); hv7.setProgress((9999 / 10000.0)); sum += 9999; } /** * 设置星期 */ private void setWeek() { int day = calendar.get(Calendar.DAY_OF_WEEK);//当天的周数 // Toast.makeText(getActivity(), day + , Toast.LENGTH_LONG).show(); day -= 1; day1.setText(week(day)); day2.setText(week(day - 1)); day3.setText(week(day - 2)); day4.setText(week(day - 3)); day5.setText(week(day - 4)); day6.setText(week(day - 5)); day7.setText(week(day - 6)); } @Override public void onClick(View arg0) { switch (arg0.getId()) { case R.id.map1: hv1.setText(true); hv2.setText(false); hv3.setText(false); hv4.setText(false); hv5.setText(false); hv6.setText(false); hv7.setText(false); view.invalidate(); break; case R.id.map2: hv1.setText(false); hv2.setText(true); hv3.setText(false); hv4.setText(false); hv5.setText(false); hv6.setText(false); hv7.setText(false); view.invalidate(); break; case R.id.map3: hv1.setText(false); hv2.setText(false); hv3.setText(true); hv4.setText(false); hv5.setText(false); hv6.setText(false); hv7.setText(false); view.invalidate(); break; case R.id.map4: hv1.setText(false); hv2.setText(false); hv3.setText(false); hv4.setText(true); hv5.setText(false); hv6.setText(false); hv7.setText(false); view.invalidate(); break; case R.id.map5: hv1.setText(false); hv2.setText(false); hv3.setText(false); hv4.setText(false); hv5.setText(true); hv6.setText(false); hv7.setText(false); view.invalidate(); break; case R.id.map6: hv1.setText(false); hv2.setText(false); hv3.setText(false); hv4.setText(false); hv5.setText(false); hv6.setText(true); hv7.setText(false); view.invalidate(); break; case R.id.map7: hv1.setText(false); hv2.setText(false); hv3.setText(false); hv4.setText(false); hv5.setText(false); hv6.setText(false); hv7.setText(true); view.invalidate(); break; default: break; } } /** * 将星期由阿拉伯数字变为汉字 * @param day * @return */ private String week(int day) { if (day < 1) { day += 7; } switch (day) { case 1: return 周一; case 2: return 周二; case 3: return 周三; case 4: return 周四; case 5: return 周五; case 6: return 周六; case 7: return 周日; default: return ; } } /** * 自定义的一个动画类 * @author 李垭超 * */ private class AllAnimation extends Animation { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if (interpolatedTime < 1.0f) { sum1 = (int) (sum * interpolatedTime); average1 = (int) (average * interpolatedTime); } else { sum1 = sum; average1 = average; } view.postInvalidate(); sum_step.setText(sum1 + ); average = sum / 7; average_step.setText(average1 + ); } } }
先给几张效果图:
如上图所示,点击中间的圆圈会依次显示3个不同的页面(信息),而且圆形的进度条是动态增加的,效果还是可以的。然后给出源代码,供大家参考
CircleBar的主要代码
package com.example.histogram.widet; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.view.View; import android.view.animation.Animation; import android.view.animation.Transformation; /** * 这是继承view的一个重新绘图的圆圈的一个类 Author: liyachao email:296777513@qq.com Date: 2015-1-5 * Time: 下午2:39 */ public class CircleBar extends View { private RectF mColorWheelRectangle = new RectF();// 圆圈的矩形范围 private Paint mDefaultWheelPaint;// 绘制底部灰色圆圈的画笔 private Paint mColorWheelPaint;// 绘制蓝色扇形的画笔 private Paint textPaint;// 中间文字的画笔 private Paint textPaint1;// 上下文字的画笔 private float mColorWheelRadius;// 圆圈普通状态下的半径 private float circleStrokeWidth;// 圆圈的线条粗细 private float pressExtraStrokeWidth;// 按下状态下增加的圆圈线条增加的粗细 private int mText;// 中间文字内容 private int mCount;// 为了达到数字增加效果而添加的变量,他和mText其实代表一个意思 private float mProgressAni;// 为了达到蓝色扇形增加效果而添加的变量,他和mProgress其实代表一个意思 private float mProgress;// 扇形弧度 private int mTextSize;// 中间文字大小 private int mTextSize1;// 上下文字大小 private int mDistance;// 上下文字的距离 BarAnimation anim;// 动画类 private int mType;// 根据传入的数值判断应该显示的页面 public CircleBar(Context context) { super(context); init(); } public CircleBar(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CircleBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { circleStrokeWidth = dip2px(getContext(), 20);// 圆圈的线条粗细 pressExtraStrokeWidth = dip2px(getContext(), 2);// 按下状态下增加的圆圈线条增加的粗细 mTextSize = dip2px(getContext(), 80);// 中间文字大小 mTextSize1 = dip2px(getContext(), 30);// 上下文字大小 mDistance = dip2px(getContext(), 70);//文字间的距离 // 绘制蓝色扇形的画笔 mColorWheelPaint = new Paint(); mColorWheelPaint.setAntiAlias(true);// 抗锯齿 mColorWheelPaint.setColor(0xFF29a6f6);// 设置颜色 mColorWheelPaint.setStyle(Paint.Style.STROKE);// 设置空心 mColorWheelPaint.setStrokeWidth(circleStrokeWidth);// 设置圆圈粗细 // 绘制底部灰色圆圈的画笔 mDefaultWheelPaint = new Paint(); mDefaultWheelPaint.setAntiAlias(true); mDefaultWheelPaint.setColor(Color.parseColor("#d9d6c3")); mDefaultWheelPaint.setStyle(Paint.Style.STROKE); mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth); // 中间文字的画笔 textPaint = new Paint(Paint.LINEAR_TEXT_FLAG); textPaint.setAntiAlias(true); textPaint.setColor(Color.parseColor("#6DCAEC")); textPaint.setStyle(Style.FILL_AND_STROKE); textPaint.setTextAlign(Align.LEFT); textPaint.setTextSize(mTextSize); // 上下文字的画笔 textPaint1 = new Paint(Paint.LINEAR_TEXT_FLAG); textPaint1.setAntiAlias(true); textPaint1.setColor(Color.parseColor("#a1a3a6")); textPaint1.setStyle(Style.FILL_AND_STROKE); textPaint1.setTextAlign(Align.LEFT); textPaint1.setTextSize(mTextSize1); // 中间文字内容 mText = 0; // 扇形弧度 mProgress = 0; // 动画类 anim = new BarAnimation(); anim.setDuration(1000); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { int halfHeight = getHeight() / 2; int halfWidth = getWidth() / 2; int radius = halfHeight < halfWidth ? halfHeight : halfWidth; // 圆圈的矩形范围 绘制底部灰色圆圈的画笔 canvas.drawCircle(halfWidth, halfHeight, radius - 20f, mDefaultWheelPaint); // canvas.drawArc(mColorWheelRectangle, -90, 360, false, // mDefaultWheelPaint); // 为了达到蓝色扇形增加效果而添加的变量,他和mSweepAngle其实代表一个意思 // mColorWheelRectangle是绘制蓝色扇形的画笔 mColorWheelRectangle.top = halfHeight - radius + 20f; mColorWheelRectangle.bottom = halfHeight + radius - 20f; mColorWheelRectangle.left = halfWidth - radius + 20f; mColorWheelRectangle.right = halfWidth + radius - 20f; // 根据mProgressAni(角度)画扇形 canvas.drawArc(mColorWheelRectangle, -90, mProgressAni, false, mColorWheelPaint); Rect bounds = new Rect(); String middleText = null;// 中间的文字 String upText = null;// 上面文字 String downText = null;// 底部文字 if (this.mType == 1) {// 第一个页面 upText = "步数"; downText = "目标:10000"; middleText = String.valueOf(mCount); } else if (this.mType == 2) {// 第二个页面 upText = "卡路里"; downText = "目标:10000"; middleText = String.valueOf(mCount); } else if (this.mType == 3) {// 第三个页面 upText = "根据自己的需要填写"; downText = "3"; middleText = "天气"; } // 中间文字的画笔 textPaint.getTextBounds(middleText, 0, middleText.length(), bounds); // drawText各个属性的意思(文字,x坐标,y坐标,画笔) canvas.drawText(middleText, (mColorWheelRectangle.centerX()) - (textPaint.measureText(middleText) / 2), mColorWheelRectangle.centerY() + bounds.height() / 2, textPaint); textPaint1.getTextBounds(upText, 0, upText.length(), bounds); canvas.drawText( upText, (mColorWheelRectangle.centerX()) - (textPaint1.measureText(upText) / 2), mColorWheelRectangle.centerY() + bounds.height() / 2 - mDistance, textPaint1); textPaint1.getTextBounds(downText, 0, downText.length(), bounds); canvas.drawText(downText, (mColorWheelRectangle.centerX()) - (textPaint1.measureText(downText) / 2), mColorWheelRectangle.centerY() + bounds.height() / 2 + mDistance, textPaint1); } // 测量父布局的大小 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); int min = Math.min(width, height); setMeasuredDimension(min, min); mColorWheelRadius = min - circleStrokeWidth - pressExtraStrokeWidth; // set方法的参数意思:left,top,right,bottom mColorWheelRectangle.set(circleStrokeWidth + pressExtraStrokeWidth, circleStrokeWidth + pressExtraStrokeWidth, mColorWheelRadius, mColorWheelRadius); } // 对外的一个接口,用来开启动画 public void startCustomAnimation() { this.startAnimation(anim); } // 中间的数值 public void setText(int text) { mText = text; this.postInvalidate();// 可以用子线程更新视图的方法调用。 } // 设置圆圈的进度和圆圈所显示的第几个页面 public void setProgress(float progress, int mType) { mProgress = progress; this.mType = mType; this.postInvalidate();// 可以用子线程更新视图的方法调用。 } /** * 继承animation的一个动画类 * * @author liyachao * */ public class BarAnimation extends Animation { /** * Initializes expand collapse animation, has two types, collapse (1) * and expand (0). * * @param view * The view to animate * @param type * The type of animation: 0 will expand from gone and 0 size * to visible and layout size defined in xml. 1 will collapse * view and set to gone */ @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if (interpolatedTime < 1.0f) { mProgressAni = interpolatedTime * mProgress; mCount = (int) (interpolatedTime * mText); } else { mProgressAni = mProgress; mCount = mText; } postInvalidate(); } } public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } }
FragmentPedometer 的代码如下
package com.example.histogram; import com.example.changepage1.R; import com.example.histogram.widet.CircleBar; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; /** * 这是记步的碎片 * Author: 李垭超 email:296777513@qq.com * Date: 2015-1-2 * Time: 下午2:39 */ public class FragmentPedometer extends Fragment{ private View view; private CircleBar circleBar; private int type = 1;//页面类型 //需要在handler里修改UI private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if (type == 1) { circleBar.setProgress(270, 1); circleBar.setText(1000); circleBar.startCustomAnimation(); } else if (type == 2) { circleBar.setProgress(180, 2); circleBar.setText(1500); circleBar.startCustomAnimation(); } else if (type == 3) { circleBar.setProgress(360, 3); circleBar.startCustomAnimation(); } }; }; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.pedometer, container, false); circleBar = (CircleBar) view.findViewById(R.id.circle); circleBar.setProgress(270, 1); circleBar.setText(1000); circleBar.startCustomAnimation();//开启动画 circleBar.setOnClickListener(new OnClickListener() { public void onClick(View view) { if (type == 1) { type = 2; } else if (type == 2) { type = 3; } else if (type == 3) { type = 1; } Message msg = new Message(); handler.sendMessage(msg); } }); return view; } }
先上几张效果图:(效果和上一篇讲到的CircleBar非常的相似,因为记步功能在后台)
如图所示,能根据你的一些基本参数,来记步。有一个缺点,因为这个是根据感应加速度来计算是否走一步,所以你在原地晃手机,也会记步,不过正常的走路还是挺准确的。
首先给出StepDetector类的代码:
package com.example.histogram.widet;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
/**
* 这是一个实现了信号监听的记步的类
* 这是从谷歌找来的一个记步的算法,看不太懂
* @author Liyachao Date:2015-1-6
*
*/
public class StepDetector implements SensorEventListener {
public static int CURRENT_SETP = 0;
public static float SENSITIVITY = 10; // SENSITIVITY灵敏度
private float mLastValues[] = new float[3 * 2];
private float mScale[] = new float[2];
private float mYOffset;
private static long end = 0;
private static long start = 0;
/**
* 最后加速度方向
*/
private float mLastDirections[] = new float[3 * 2];
private float mLastExtremes[][] = { new float[3 * 2], new float[3 * 2] };
private float mLastDiff[] = new float[3 * 2];
private int mLastMatch = -1;
/**
* 传入上下文的构造函数
*
* @param context
*/
public StepDetector(Context context) {
super();
int h = 480;
mYOffset = h * 0.5f;
mScale[0] = -(h * 0.5f * (1.0f / (SensorManager.STANDARD_GRAVITY * 2)));
mScale[1] = -(h * 0.5f * (1.0f / (SensorManager.MAGNETIC_FIELD_EARTH_MAX)));
}
//当传感器检测到的数值发生变化时就会调用这个方法
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
synchronized (this) {
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float vSum = 0;
for (int i = 0; i < 3; i++) {
final float v = mYOffset + event.values[i] * mScale[1];
vSum += v;
}
int k = 0;
float v = vSum / 3;
float direction = (v > mLastValues[k] ? 1
: (v < mLastValues[k] ? -1 : 0));
if (direction == -mLastDirections[k]) {
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or
// maximum?
mLastExtremes[extType][k] = mLastValues[k];
float diff = Math.abs(mLastExtremes[extType][k]
- mLastExtremes[1 - extType][k]);
if (diff > SENSITIVITY) {
boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k] * 2 / 3);
boolean isPreviousLargeEnough = mLastDiff[k] > (diff / 3);
boolean isNotContra = (mLastMatch != 1 - extType);
if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough
&& isNotContra) {
end = System.currentTimeMillis();
if (end - start > 500) {// 此时判断为走了一步
CURRENT_SETP++;
mLastMatch = extType;
start = end;
}
} else {
mLastMatch = -1;
}
}
mLastDiff[k] = diff;
}
mLastDirections[k] = direction;
mLastValues[k] = v;
}
}
}
//当传感器的经度发生变化时就会调用这个方法,在这里没有用
public void onAccuracyChanged(Sensor arg0, int arg1) {
}
}
下来是后台服务StepService的代码:
package com.example.histogram.widet;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.IBinder;
public class StepService extends Service {
public static Boolean flag = false;
private SensorManager sensorManager;
private StepDetector stepDetector;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
//这里开启了一个线程,因为后台服务也是在主线程中进行,这样可以安全点,防止主线程阻塞
new Thread(new Runnable() {
public void run() {
startStepDetector();
}
}).start();
}
private void startStepDetector() {
flag = true;
stepDetector = new StepDetector(this);
sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);//获取传感器管理器的实例
Sensor sensor = sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//获得传感器的类型,这里获得的类型是加速度传感器
//此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率
sensorManager.registerListener(stepDetector, sensor,
SensorManager.SENSOR_DELAY_FASTEST);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
flag = false;
if (stepDetector != null) {
sensorManager.unregisterListener(stepDetector);
}
}
}
最后把FragmentPedometer测试页面的代码也给大家,如果大家看过之前的博客,应该知道这是一个碎片。如果没看过,看兴趣的朋友可以看看之前的博文:
package com.example.histogram;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.example.changepage1.R;
import com.example.histogram.widet.CircleBar;
import com.example.histogram.widet.StepDetector;
import com.example.histogram.widet.StepService;
import com.example.histogram.widet.Weather;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
/**
* 这是记步的碎片
* Author: 李垭超 email:296777513@qq.com
* Date: 2015-1-6
* Time: 下午8:39
*/
public class FragmentPedometer extends Fragment implements OnClickListener{
private View view;
private CircleBar circleBar;
private int total_step = 0;
private Thread thread;
private int Type = 1;
private int calories = 0;
private int step_length = 50;
private int weight = 70;
private Weather weather;
private String test;
private boolean flag = true;// 来判断第三个页面是否开启动画
@SuppressLint(HandlerLeak)
Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
total_step = StepDetector.CURRENT_SETP;
if (Type == 1) {
circleBar.setProgress(total_step, Type);
} else if (Type == 2) {
calories = (int) (weight * total_step * step_length * 0.01 * 0.01);
circleBar.setProgress(calories, Type);
} else if (Type == 3) {
if (flag) {
circleBar.startCustomAnimation();
flag = false;
}
if (test != null || weather.getWeather() == null) {
weather.setWeather(正在更新中...);
weather.setPtime();
weather.setTemp1();
weather.setTemp2();
circleBar.startCustomAnimation();
circleBar.setWeather(weather);
} else {
circleBar.setWeather(weather);
}
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.pedometer, container, false);
init();
mThread();
return view;
}
private void init() {
Intent intent = new Intent(getActivity(), StepService.class);
getActivity().startService(intent);
weather = new Weather();
circleBar = (CircleBar) view.findViewById(R.id.progress_pedometer);
circleBar.setMax(10000);
circleBar.setProgress(StepDetector.CURRENT_SETP, 1);
circleBar.startCustomAnimation();
circleBar.setOnClickListener(this);
}
private void mThread() {
if (thread == null) {
thread = new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (StepService.flag) {
Message msg = new Message();
handler.sendMessage(msg);
}
}
}
});
thread.start();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.progress_pedometer:
if (Type == 1) {
Type = 2;
} else if (Type == 2) {
flag = true;
Type = 3;
} else if (Type == 3) {
Type = 1;
}
Message msg = new Message();
handler.sendMessage(msg);
break;
default:
break;
}
}