天天看点

Android自定义控件之会缩放的GridView Android中GridView中onTouch监听(2)自己写的Scroll滑动判断

 发现之前的东西些复杂

Android中GridView中onTouch监听(1)每个item进行点击缩放动画的制作以及点击事件的监听

Android中GridView中onTouch监听(2)自己写的Scroll滑动判断

现在把它写成一个自定义控件,方便扩展。

效果如下:

Android自定义控件之会缩放的GridView Android中GridView中onTouch监听(2)自己写的Scroll滑动判断

主要就是在自定义控件的onTouchEvent中写自己的动画效果

代码如下:

package com.example.mygridviewdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.GridView;

/**
 * 实现点击 item做缩放动画
 * 
 * @author leafact
 * 
 */
public class TouchableGridView extends GridView {

	private Context context;
	// action down按下动画
	private static Animation downAnimation;
	// action up动画
	private static Animation upAnimation;
	// 前一个元素
	private static int tempChildViewId = -1;
	private static int downChildViewId = -1;
	private static int upChildViewId = -2;

	private static boolean isLastView = true;

	private static final int numColumns = 3;

	public TouchableGridView(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.context = context;

	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		showAnimation(ev);
		// 扩展重写
		return super.onTouchEvent(ev);
	}

	private void showAnimation(MotionEvent event) {

		// 判断是不是GridView的v
		if (!(this instanceof GridView))
			return;
		GridView parent = ((GridView) this);
		int count = parent.getChildCount();
		// 没有元素不做动画
		if (count == 0)
			return;

		// 获得每个元素的大小。这里每个gridView的元素都是相同大小的,取第一个为例。
		int childWidth = parent.getChildAt(0).getWidth() + 10;
		int childHeight = parent.getChildAt(0).getHeight() + 2;
		Log.d("count", "==" + count);
		// 进行事件监听
		switch (event.getAction()) {
		// 按下的时候,获得当前元素的id,由于我一行是numColumns个,所以我的y方向上就必须乘以numColumns
		// 例如我按下的是第numColumns个(从第0个开始,第三个为第二行第一列),那么第三个=点击x的大小/子元素的x的大小+(点击y的大小/子元素的y的大小)*numColumns=0+numColumns=numColumns、
		case MotionEvent.ACTION_DOWN: {
			// 重置
			tempChildViewId = -1;
			downChildViewId = -1;
			upChildViewId = -2;
			isLastView = true;

			// 三目运算符
			int currentChildViewId = ((int) event.getX() / childWidth + ((int) event
					.getY() + parent.getScrollY()) / childHeight * numColumns) < count ? ((int) event
					.getX() / childWidth + ((int) event.getY() + parent
					.getScrollY()) / childHeight * numColumns)
					: -1;
			// 开始按没按在存在的元素中的时候这个动画不做
			if (currentChildViewId == -1)
				return;
			// 进行缩小动画
			downAnimation = AnimationUtils.loadAnimation(context,
					R.anim.backgroundanimdown);
			parent.getChildAt(currentChildViewId).startAnimation(downAnimation);
			tempChildViewId = currentChildViewId;
			downChildViewId = currentChildViewId;
			break;
		}
		// 通过位置判断是哪个item,做对应的动画
		case MotionEvent.ACTION_MOVE: {
			// 计算出当前chidView的位于gridView中的位置
			int currentChildViewId = ((int) event.getX() / childWidth + ((int) event
					.getY() + parent.getScrollY()) / childHeight * numColumns) < count ? ((int) event
					.getX() / childWidth + ((int) event.getY() + parent
					.getScrollY()) / childHeight * numColumns)
					: -1;
			// 当之前一个元素存在,而移动到不存在元素的区域的时候,需要立即将之前图片抬起
			// 由于这个方法只让他走一次。设置了一个isLastView的boolean参数
			if (tempChildViewId != -1 && currentChildViewId == -1 && isLastView) {
				Log.d("movemove", "movemove");
				isLastView = false;
				upAnimation = AnimationUtils.loadAnimation(context,
						R.anim.backgroundanimup);
				parent.getChildAt(tempChildViewId).startAnimation(upAnimation);
				return;
			}
			if (currentChildViewId == -1)
				return;
			// 当前元素与之前元素相同,不执行变化操作。
			if (currentChildViewId != tempChildViewId) {
				// 表示从不存在的元素移动到存在的元素的时候。只需要做按下操作即可
				if (tempChildViewId == -1) {
					downAnimation = AnimationUtils.loadAnimation(context,
							R.anim.backgroundanimdown);
					parent.getChildAt(currentChildViewId).startAnimation(
							downAnimation);
				} else {
					// 表示从存在的元素移动到另外一个存在的元素的时候。只需要做按下操作即可
					// 原来的动画变成弹起。之后的那个执行新的动画
					upAnimation = AnimationUtils.loadAnimation(context,
							R.anim.backgroundanimup);
					parent.getChildAt(tempChildViewId).startAnimation(
							upAnimation);
					downAnimation = AnimationUtils.loadAnimation(context,
							R.anim.backgroundanimdown);
					parent.getChildAt(currentChildViewId).startAnimation(
							downAnimation);
				}
				// 改变前一个元素的位置。
				tempChildViewId = currentChildViewId;
			}
			break;
		}
		// 抬起,松手的时候
		case MotionEvent.ACTION_UP: {
			int currentChildViewId = ((int) event.getX() / childWidth + ((int) event
					.getY() + parent.getScrollY()) / childHeight * numColumns) < count ? ((int) event
					.getX() / childWidth + ((int) event.getY() + parent
					.getScrollY()) / childHeight * numColumns)
					: -1;
			Log.d("currentChildViewId", currentChildViewId + "");
			// 按下和抬起都在无效位置的话,do nothing
			if (currentChildViewId == -1)
				return;
			// 其他情况下,需要收起当前的动画
			upAnimation = AnimationUtils.loadAnimation(context,
					R.anim.backgroundanimup);
			parent.getChildAt(currentChildViewId).startAnimation(upAnimation);
			upChildViewId = currentChildViewId;
		}
		default:
			break;
		}

	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
	}
}
           

注意这里的onTouchEvent 中返回值,是调用super的方法(父类的方法),也就是扩展重写,如果返回true,拦截的话,之后的滑动以及点击事件也要自己重新写,但是这样写就不需要了。

两个动画写在anim文件夹中

按下的动画:缩小

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="1"
    android:fromYScale="1"
    android:toXScale="0.8"
    android:toYScale="0.8"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true"
    android:duration="300"
     />
           

离开时候的动画,放大

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="0.8"
    android:fromYScale="0.8"
    android:toXScale="1"
    android:toYScale="1"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true"
    android:duration="500"
     />
           

mainActivity里面

package com.example.mygridviewdemo;

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

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Toast;

public class MainActivity extends Activity {

	private TouchableGridView touchableGridView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		touchableGridView = (TouchableGridView) findViewById(R.id.touchableGridView);
		List<String> data = new ArrayList<String>();
		for (int i = 0; i < 30; i++) {
			data.add("" + i);
		}
		touchableGridView.setAdapter(new TypeGridViewAdapter(this, data));
		touchableGridView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				Toast.makeText(MainActivity.this, "onItemClick",
						Toast.LENGTH_SHORT).show();
			}
		});
	}

}
           

xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
    
	<!-- numColumns为列数-->
	<com.example.mygridviewdemo.TouchableGridView 
	    android:layout_width="fill_parent"
	    android:layout_height="fill_parent"
	    android:numColumns="3"
	    android:horizontalSpacing="2dp"
        android:verticalSpacing="2dp"
	    android:id="@+id/touchableGridView"
	    />
</RelativeLayout>
           

当然还需要自定义一个适配器,里面设置每个item的背景颜色和文本内容,以及对于item的xml布局,这里就累赘了。

===================

之后的学习发现原来要获取选择的item的id,其实安卓提供了方法

/**
     * Maps a point to a position in the list.
     *
     * @param x X in local coordinate
     * @param y Y in local coordinate
     * @return The position of the item which contains the specified point, or
     *         {@link #INVALID_POSITION} if the point does not intersect an item.
     */
    public int pointToPosition(int x, int y) {
        Rect frame = mTouchFrame;
        if (frame == null) {
            mTouchFrame = new Rect();
            frame = mTouchFrame;
        }

        final int count = getChildCount();
        for (int i = count - 1; i >= 0; i--) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.VISIBLE) {
                child.getHitRect(frame);
                if (frame.contains(x, y)) {
                    return mFirstPosition + i;
                }
            }
        }
        return INVALID_POSITION;
    }
           

即currentChildViewId=pointToPosition(ev.getX(),ev.getY());