最近在学习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触屏滑动事件总结 (2011-08-08 10:14:25)
  2. 《Android 开发艺术探索》笔记——(3)View 的事件体系
  3. 修改AVD存放位置
  4. android MapView 定位与Overlay onTap事件处理
  5. 在任意位置获取应用程序Context
  6. Android中EventBus(事件总线)传递数据
  7. 〖Android〗屏幕触屏事件录制与回放

随机推荐

  1. android之线程访问网络并解析数据
  2. Android中判断网络连接是否可用的方法总
  3. Android获取系统储存以及内存信息的方法(
  4. android实现手机传感器调用
  5. Nexus刷Android6.0
  6. 8.1Launcher3 去掉桌面底部渐白色部分
  7. android ssl-dl
  8. Android(安卓)创建fragment时向fragment
  9. Android原生集成react-native(二)-热更新
  10. React-native 集成react-native-getui 爬