最近在学习android的ViewGroup,然后就想弄个例子练练手。看到网上的瀑布流挺有意思的,就尝试着去做。弄了一天终于弄好。

android的自定义ViewGroup不多说,继承ViewGroup并覆写onLayout和onMeasure方法。在onLayout方法中设置child的布局位置,在onMeasure中测量child的宽高。以下是瀑布流的实现代码:(WaterFall写成WaterFlow, = =!....)

package com.example.waterflowdemo;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.GestureDetector.OnGestureListener;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Toast;public class WaterFlow extends ViewGroup implements View.OnClickListener, OnGestureListener {private static final String TAG = WaterFlow.class.getSimpleName();private static final int MARGIN = 2;private Context context;private GestureDetector gestureDetector;/* * this view group's height */private int viewGroupHeight = 0;/* * position needed to scroll */private float position = 0;/* * last down position */private float downPosition = 0;private static final int COLUMNS = 2;public WaterFlow(Context context) {super(context);Log.d(TAG, "WaterFlow constructor");this.context = context;init();}public WaterFlow(Context context, AttributeSet attrs) {super(context, attrs);Log.d(TAG, "WaterFlow constructor");this.context = context;init();}private void init() {gestureDetector = new GestureDetector(context, this);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {Log.d(TAG, "WaterFlow.onLayout()");final int width = r - l;final int height = b - t;final int childCount = getChildCount();int y1 = t;int y2 = t;for(int i = 0; i < childCount; i ++) {final View child = getChildAt(i);child.setTag(i);child.setOnClickListener(this);final int childWidth = child.getMeasuredWidth();final int childHeight = child.getMeasuredHeight();//Log.d(TAG, "childWidth: " + childWidth + ", childHeight: " + childHeight);double scale = ((double)childWidth) / (width / COLUMNS);//Log.d(TAG, "scale: " + scale);final int w = width / COLUMNS;int h = 0;if(scale != 0) {h = (int) (childHeight / scale);} else {h = childHeight;}//Log.d(TAG, "w: " + w + ", h: " + h);if(y1 <= y2) {child.layout(l + MARGIN, y1 + MARGIN, l + w - MARGIN, y1 + h - MARGIN);y1 += h;//Log.d(TAG, "y1: " + y1);} else {child.layout(l + w + MARGIN, y2 + MARGIN, r - MARGIN, y2 + h - MARGIN);y2 += h;//Log.d(TAG, "y2: " + y2);}}viewGroupHeight = y1 > y2 ? y1 : y2;Log.d(TAG, "ViewGroupHeight: " + viewGroupHeight);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {Log.d(TAG, "WaterFlow.onMeasure()");final int childCount = getChildCount();int arg0 = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);int arg1 = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);for(int i = 0; i < childCount; i ++) {View child = getChildAt(i);if(child.getVisibility() != View.GONE) {child.measure(arg0, arg1);}}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overridepublic boolean onTouchEvent(MotionEvent event) {int height = getMeasuredHeight();if(viewGroupHeight <= height) {return false;}switch(event.getAction()) {case MotionEvent.ACTION_DOWN:downPosition = event.getRawY();break;case MotionEvent.ACTION_MOVE:float transLength = event.getRawY() - downPosition;Log.d(TAG, "trans: " + transLength);if(transLength != 0) {position -= transLength;if(position < 0) {position = 0;} else if(position > viewGroupHeight - height) {position = viewGroupHeight - height;}Log.d(TAG, "position: " + position);this.scrollTo(0, (int) position);}downPosition = event.getRawY();break;default:}return gestureDetector.onTouchEvent(event);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch(ev.getAction()) {case MotionEvent.ACTION_DOWN:onTouchEvent(ev);return false;case MotionEvent.ACTION_MOVE:return true;case MotionEvent.ACTION_UP:return false;}return false;}@Overridepublic void onClick(View v) {int i = (Integer) v.getTag();Toast.makeText(context, "Number " + i, Toast.LENGTH_SHORT).show();}@Overridepublic boolean onDown(MotionEvent e) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {Log.d(TAG, "onFiling");return false;}@Overridepublic void onLongPress(MotionEvent e) {// TODO Auto-generated method stub}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {Log.d(TAG, "onScroll");return true;}@Overridepublic void onShowPress(MotionEvent e) {// TODO Auto-generated method stub}@Overridepublic boolean onSingleTapUp(MotionEvent e) {// TODO Auto-generated method stubreturn false;}}

onTouchEvent处理了瀑布流的滚动。如果做效果好一点可以添加手势操作,在onFiling中加入快速滑动等。可以在onTouchEvent中将MotionEvent交由GestureDetector处理。onClick响应了子view的点击事件,在这里只是简单地弹出一个toast并标注是第几个。

解决onTouch和onClick的冲突
昨天在网上看个一哥们的博文,看到了android关于onClick、onLongClick和onTouch的机制,在此多谢那位兄弟了。onClick是在onTouch中的MotionEvent.ACTION_DWON和MotionEvent.ACTION_UP中处理,在UP中会判断touch位置是否改变,touch的位置是否还在同一个view上,然后决定是否响应onClick时间;onLongClick是直接在MotionEvent.ACTION_DOWN中判断,当超过一定时间后就会响应onLongClick。所以在 onIntercepTouchEvent 中在DOWN中直接执行一次本view的onTouchEvent,然后返回false;在MOVE中返回true由本view拦截消耗事件;在UP中返回false将事件传递到子view中。

下面是测试的Activity代码,
package com.example.waterflowdemo;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.app.Activity;import android.graphics.Color;import android.view.Menu;import android.view.ViewGroup;import android.widget.ImageView;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private WaterFlow waterFlow;private AddViewThread addViewThread;static Handler handler = new Handler() {private int index = 0;@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if(msg.what == 0 && msg.obj != null && msg.obj instanceof WaterFlow) {WaterFlow waterFlow = (WaterFlow) msg.obj;ImageView image = new ImageView(waterFlow.getContext());image.setImageResource(R.drawable.d1 + (index % 22));image.setBackgroundColor(Color.GRAY);ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);lp.setMargins(2, 2, 2, 2);image.setLayoutParams(lp);image.setTag(index);waterFlow.addView(image);index ++;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);waterFlow = (WaterFlow) findViewById(R.id.waterflow);setContentView(waterFlow = (WaterFlow) findViewById(R.id.waterflow));(addViewThread = new AddViewThread()).start();}@Overrideprotected void onDestroy() {addViewThread.flag = false;super.onDestroy();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}class AddViewThread extends Thread {boolean flag = true;public void run() {while(flag) {Message msg = handler.obtainMessage(0, waterFlow);handler.sendMessage(msg);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}}}

最后图片过多的话,应该就会产生OOM的问题。这个就要做图片的及时回收了,这个可以参照google官方的那个Demo解决。以上就这么多了,接下来去研究研究ViewGroup和动画结合的效果了。









更多相关文章

  1. android - 为响应度而设计 - 开发文档翻译
  2. 我的android 第31天 - Activity(四)
  3. android view滑动助手类OverScroller
  4. android瀑布流简单实现原理
  5. android - 为响应度而设计 - 开发文档翻译
  6. Android(安卓)开发资料总结--响应式编程
  7. Android开发之实现图片自动滚动显示标签的ViewPager
  8. Android(安卓)Google Map实例 - 添加GPS位置标注(Android(安卓)m
  9. gravity和layout_gravity

随机推荐

  1. 深度探索Android应用程序的基本原理
  2. Android(安卓)手机十大必备软件
  3. android运行C程序以及C程序在android下的
  4. 深入探索Android卡顿优化(下)
  5. 如何检索Android设备的唯一ID
  6. Android(安卓)开发者从0到1发布一个微信
  7. 搭建自己的android 开发环境 (eclipse +
  8. Android(安卓)IPC机制(三)在Android(安卓)S
  9. MegaPad,23 英寸的 Android(安卓)平板?[视
  10. [置顶] Android学习及如何利用android来