转载请注明出处,谢谢:http://blog.csdn.net/harryweasley/article/details/46858019
提前声明:本篇博客是基于电视机顶盒的,全部操作是用遥控器。
我要实现这样的一个效果。点击gridview的item,有放大的效果。
先看下效果图。
本篇博客的重难点:
1.怎么让选中的item变大。
2.变大后,怎么让item全部显示出来,而不是被gridview的边缘挡住。
3.怎么每次进入gridview当前item变大。即解决setOnItemSelectedListener不响应的方法。
我先将我的代码分开,来分别讲解着三个重点,之后会将整个源码上传。
1.怎么让选中的item变大。
在BaseAdapter里的getView方法中,进行如下的判断。
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- convertView = LayoutInflater.from(context).inflate(
- R.layout.item_app_wall2, parent, false);
- holder = new ViewHolder();
- holder.icon = (ImageView) convertView
- .findViewById(R.id.app_icon);
- holder.name = (TextView) convertView
- .findViewById(R.id.app_name);
- holder.size = (TextView) convertView
- .findViewById(R.id.app_size);
- holder.count = (TextView) convertView
- .findViewById(R.id.app_count);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- if (selected == position) {
- // 如果选中的是当前item,则将当前item变大。
- convertView.bringToFront();
- animEffect.setAttributs(1.0F, 1.0366F, 1.0F, 1.0366F, 100L);
- Animation localAnimation = animEffect.createAnimation();
- convertView.startAnimation(localAnimation);
- }
- if (last == position) {
- // 将上一个选中的item恢复原样。
- animEffect.setAttributs(1.0366F, 1.0F, 1.0366F, 1.0F, 0L);
- convertView.startAnimation(animEffect.createAnimation());
- }
- return convertView;
- }
然后在代码里,调用appWallBaseAdapter.notifyDataSetChanged()该方法。如下所示:
- appGrid.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int position, long id) {
- Log.i("tag", "OnItemSelectedListener执行了");
- isSelect = true;
- last = selected;
- selected = position;
- appWallBaseAdapter.notifyDataSetChanged();
- appGrid.smoothScrollToPositionFromTop(position, 300);
- }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- Log.i("tag", "");
- }
- });
如果是当前选中的item,则直接将convertView变大。其中animEffect是自定义的。
- /**
- * 自定义动画类
- */
- ScaleAnimEffect animEffect = new ScaleAnimEffect();
ScaleAnimEffect类里的内容是:
- package com.example.test;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.Animation;
- import android.view.animation.ScaleAnimation;
- public class ScaleAnimEffect {
- private long duration;
- private float fromXScale;
- private float fromYScale;
- private float toXScale;
- private float toYScale;
- public void setAttributs(float paramFloat1, float paramFloat2,
- float paramFloat3, float paramFloat4, long paramLong) {
- this.fromXScale = paramFloat1;
- this.fromYScale = paramFloat3;
- this.toXScale = paramFloat2;
- this.toYScale = paramFloat4;
- this.duration = paramLong;
- }
- public Animation createAnimation() {
- /**
- * @param fromX
- * 动画开始前水平方向的伸缩比例大小
- * @param toX
- * 动画结束后,水平方向的伸缩比例大小
- * @param fromY
- * 动画开始前,竖直方向的比例大小
- * @param toY
- * 动画结束结束后,竖直方向的比例大小
- * @param pivotXType
- * 指定pivotXValue以哪个为坐标点为中心来旋转。 Animation.ABSOLUTE,
- * Animation.RELATIVE_TO_SELF, 或者
- * Animation.RELATIVE_TO_PARENT这三个其中之一。
- * @param pivotXValue
- * 正在伸缩的对象的点的x坐标,指定为绝对数,并且0是左边缘(当对象改变尺寸的时候,点保持不变。)
- * 如果pivotXType是
- * Animation.ABSOLUTE,这个值既可以是绝对数,也可以为百分数(1.0位100%)
- *
- * @param pivotYType
- * 指定pivotYValue以哪个为坐标点为中心来旋转。 Animation.ABSOLUTE,
- * Animation.RELATIVE_TO_SELF, 或者
- * Animation.RELATIVE_TO_PARENT这三个其中之一。
- * @param pivotYValue
- * 正在伸缩的对象的点的y坐标,指定为绝对数,并且0是左边缘(当对象改变尺寸的时候,点保持不变。)
- * 如果pivotYType是
- * Animation.ABSOLUTE,这个值既可以是绝对数,也可以为百分数(1.0位100%)
- */
- ScaleAnimation localScaleAnimation = new ScaleAnimation(
- this.fromXScale, this.toXScale, this.fromYScale, this.toYScale,
- Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,
- 0.5F);
- // 动画执行完成后,是否停留在执行完的状态
- localScaleAnimation.setFillAfter(true);
- // 在动画开始的地方速率比较慢,然后开始加速
- localScaleAnimation.setInterpolator(new AccelerateInterpolator());
- // 设置动画持续时间
- localScaleAnimation.setDuration(this.duration);
- return localScaleAnimation;
- }
- }
因为之前的一篇文章已经写过了变大效果,所以这次便不详细叙述,你可以直接查看这篇文章。http://blog.csdn.net/harryweasley/article/details/46678315(图片获得焦点变大并显示在最前方,覆盖后面的图片)
2.变大后,怎么让item完全显示出来,而不是被gridview的边缘挡住。
我都都知道,gridview中的item是靠着其边缘的,那么在代码中,将边缘的item变大后,便会出现显示不完全的现象。如图所示:
为了避免这样的情况发生,我在item的布局中,RelativeLayout中又加入了一个RelativeLayout,里面的RelativeLayout用了margin变量,保证内容和父控件有一定的距离。如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
- <RelativeLayout
- android:layout_width="@dimen/app_item_width"
- android:layout_height="@dimen/app_item_height"
- android:layout_margin="13dp"
- android:background="@drawable/background_selector2" >
- <ImageView
- android:id="@+id/app_icon"
- android:layout_width="@dimen/detail_left"
- android:layout_height="@dimen/detail_left"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_marginLeft="30dp"
- android:src="@drawable/ic_launcher" />
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/detail_left"
- android:layout_alignBottom="@id/app_icon"
- android:layout_alignTop="@id/app_icon"
- android:layout_marginLeft="12dp"
- android:layout_toRightOf="@id/app_icon" >
- <TextView
- android:id="@+id/app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:gravity="top"
- android:text="应用名称"
- android:textColor="@color/white"
- android:textSize="@dimen/text_item_app_name" />
- <TextView
- android:id="@+id/app_size"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_below="@id/app_name"
- android:gravity="bottom"
- android:text="13M"
- android:textColor="@color/text_search"
- android:textSize="@dimen/text_search_game" />
- <TextView
- android:id="@+id/app_count"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_below="@id/app_name"
- android:layout_marginLeft="20dp"
- android:layout_toRightOf="@id/app_size"
- android:gravity="bottom"
- android:text="100次"
- android:textColor="@color/text_search"
- android:textSize="@dimen/text_search_game" />
- </RelativeLayout>
- </RelativeLayout>
- </RelativeLayout>
3.怎么每次进入gridview当前item变大。即解决setOnItemSelectedListener不响应的方法。
这个应该是最难点了的,我花了很长的时间来研究它,最终找到了一个还算可以的方法,在这里写一下。
因为我的放大效果是在setOnItemSelectedListener方法里,来进行调用的,但是,当我实测的时候,发现setOnItemSelectedListener这个方法在第一次进入的时候,有时候会 不执行,有时候又会去执行,我也找到了他不执行的规律,但是,却很难去具体研究,可能是android内部机构的问题吧。
这样就会出现问题,如果我只是在setOnItemSelectedListener这个方法里来判断是否放大的话,那么有时候item是不会放大的,这显然是不对的。为了解决这个问题,我引入了setOnFocusChangeListener这个方法,来监gridview听焦点状态改变。经过测试,我知道了,不管怎么进入和出去gridview,setOnFocusChangeListener是一定会执行的,而且是先于setOnItemSelectedListener这个方法。
我的想法是,先进行setOnItemSelectedListener来将item变大,如果这个方法没有执行,再在setOnFocusChangeListener,将item变大,为了等待setOnItemSelectedListener是否执行的消息isSelect,在setOnFocusChangeListener开启子线程,等待isSelect,如果isSelect为true,则不执行本方法里appWallBaseAdapter.notifyDataSetChanged();如果为false则执行。
setOnFocusChangeListener代码如下所示,
- appGrid.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- // 如果获取到焦点
- // 开启线程等待50ms,看是否setOnItemSelectedListener执行,没有执行则执行此方法
- new Thread(run).start();
- } else {
- last = selected;
- Log.i("tag", last + "onfoceus 出来的这里是几..............");
- selected = -1;
- appWallBaseAdapter.notifyDataSetChanged();
- isSelect = false;
- }
- }
- });
开启线程等待50ms。
- Runnable run = new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(50);
- handler.sendEmptyMessage(0);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- };
- Handler handler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- if (isSelect) {
- isSelect = false;
- } else {
- // 如果是第一次进入该gridView,则进入第一个item,如果不是第一次进去,则选择上次出来的item
- if (last == -1) {
- selected = 0;
- } else {
- selected = last;
- }
- last = -1;
- appWallBaseAdapter.notifyDataSetChanged();
- }
- };
- };
setOnItemSelectedListener方法如下所示,如果执行了则将isSelect置为true。
- appGrid.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int position, long id) {
- Log.i("tag", "OnItemSelectedListener执行了");
- isSelect = true;
- last = selected;
- selected = position;
- appWallBaseAdapter.notifyDataSetChanged();
- appGrid.smoothScrollToPositionFromTop(position, 300);
- }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- Log.i("tag", "");
- }
- });
现在整个项目便大功告成了,MainActivity里的所有代码如下所示:
- package com.example.test;
- import java.util.ArrayList;
- import android.app.Activity;
- import android.content.Context;
- import android.os.Bundle;
- import android.os.Handler;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnFocusChangeListener;
- import android.view.ViewGroup;
- import android.view.Window;
- import android.view.animation.Animation;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.AdapterView.OnItemSelectedListener;
- import android.widget.BaseAdapter;
- import android.widget.GridView;
- import android.widget.ImageView;
- import android.widget.ListView;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- /**
- * 各种应用的gridView
- */
- private GridView appGrid;
- /**
- * 界面左边的各种信息listView
- *
- */
- private ListView appWallListView;
- /**
- * 自定义动画类
- */
- ScaleAnimEffect animEffect = new ScaleAnimEffect();
- /**
- * 应用墙的适配器
- */
- AppWallBaseAdapter appWallBaseAdapter;
- /**
- * 当前选择的item位置
- */
- int selected = -1;
- /**
- * 上一次选择的item位置
- */
- int last = -1;
- /**
- * 是否onItemSelected方法执行了
- */
- boolean isSelect = false;
- /**
- * 界面左边上下的箭头图片
- */
- ImageView top_image, bottom_iamge;
- ArrayList<String> list = new ArrayList<String>();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.activity_app_wall);
- top_image = (ImageView) findViewById(R.id.top);
- bottom_iamge = (ImageView) findViewById(R.id.bottom);
- appGrid = (GridView) findViewById(R.id.app_wall);
- appGridShow();
- appWallListViewShow();
- appWallListView.requestFocus();
- }
- /**
- * 应用墙应用展示界面
- */
- private void appGridShow() {
- appWallBaseAdapter = new AppWallBaseAdapter(this);
- appGrid.setAdapter(appWallBaseAdapter);
- appGrid.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int position, long id) {
- Log.i("tag", "OnItemSelectedListener执行了");
- isSelect = true;
- last = selected;
- selected = position;
- appWallBaseAdapter.notifyDataSetChanged();
- appGrid.smoothScrollToPositionFromTop(position, 300);
- }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- Log.i("tag", "");
- }
- });
- appGrid.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- // 如果获取到焦点
- // 开启线程等待50ms,看是否setOnItemSelectedListener执行,没有执行则执行此方法
- new Thread(run).start();
- } else {
- last = selected;
- Log.i("tag", last + "onfoceus 出来的这里是几..............");
- selected = -1;
- appWallBaseAdapter.notifyDataSetChanged();
- isSelect = false;
- }
- }
- });
- }
- Runnable run = new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(50);
- handler.sendEmptyMessage(0);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- };
- Handler handler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- if (isSelect) {
- isSelect = false;
- } else {
- // 如果是第一次进入该gridView,则进入第一个item,如果不是第一次进去,则选择上次出来的item
- if (last == -1) {
- selected = 0;
- } else {
- selected = last;
- }
- last = -1;
- appWallBaseAdapter.notifyDataSetChanged();
- }
- };
- };
- class AppWallBaseAdapter extends BaseAdapter {
- private Context context;
- public AppWallBaseAdapter(Context context) {
- this.context = context;
- }
- @Override
- public int getCount() {
- return 13;
- }
- @Override
- public Object getItem(int position) {
- return null;
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- convertView = LayoutInflater.from(context).inflate(
- R.layout.item_app_wall2, parent, false);
- holder = new ViewHolder();
- holder.icon = (ImageView) convertView
- .findViewById(R.id.app_icon);
- holder.name = (TextView) convertView
- .findViewById(R.id.app_name);
- holder.size = (TextView) convertView
- .findViewById(R.id.app_size);
- holder.count = (TextView) convertView
- .findViewById(R.id.app_count);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- if (selected == position) {
- // 如果选中的是当前item,则将当前item变大。
- convertView.bringToFront();
- animEffect.setAttributs(1.0F, 1.0366F, 1.0F, 1.0366F, 100L);
- Animation localAnimation = animEffect.createAnimation();
- convertView.startAnimation(localAnimation);
- }
- if (last == position) {
- // 将上一个选中的item恢复原样。
- animEffect.setAttributs(1.0366F, 1.0F, 1.0366F, 1.0F, 0L);
- convertView.startAnimation(animEffect.createAnimation());
- }
- return convertView;
- }
- class ViewHolder {
- ImageView icon, grade;
- TextView name, size, count;
- }
- }
- /**
- * 应用墙界面左边展示
- */
- private void appWallListViewShow() {
- for (int i = 0; i < 10; i++) {
- list.add("网" + i);
- }
- if (list.size() <= 10) {
- bottom_iamge.setVisibility(View.INVISIBLE);
- }
- appWallListView = (ListView) findViewById(R.id.app_wall_listview);
- appWallListView.setAdapter(new AppWallListBaseAdapter(this, list));
- appWallListView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- }
- });
- appWallListView.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view,
- int position, long id) {
- if (position > 4) {
- top_image.setVisibility(View.VISIBLE);
- }
- if (position == list.size() - 1) {
- bottom_iamge.setVisibility(View.INVISIBLE);
- }
- if (position == 0) {
- top_image.setVisibility(View.INVISIBLE);
- }
- if (position < list.size() - 5) {
- bottom_iamge.setVisibility(View.VISIBLE);
- }
- }
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
- }
- }
activity_app_wall.xml文件如下所示:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/main_backgrand" >
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/detail_left"
- android:layout_marginTop="@dimen/programa_top"
- android:text="最新"
- android:textColor="@color/white"
- android:textSize="@dimen/text_detail_app_name" />
- <ImageView
- android:id="@+id/top"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/text"
- android:layout_marginLeft="92dp"
- android:layout_marginTop="@dimen/detail_icon_top"
- android:visibility="invisible"
- android:src="@drawable/top_focused" />
- <ImageView
- android:id="@+id/bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="@dimen/detail_left"
- android:layout_marginLeft="92dp"
- android:src="@drawable/bottom_focused" />
- <ListView
- android:id="@+id/app_wall_listview"
- android:layout_width="108dp"
- android:layout_height="wrap_content"
- android:layout_above="@id/bottom"
- android:layout_below="@id/top"
- android:layout_marginBottom="42dp"
- android:layout_marginLeft="49dp"
- android:layout_marginTop="42dp"
- android:divider="@android:color/transparent"
- android:nextFocusDown="@id/app_wall_listview"
- android:nextFocusUp="@id/app_wall_listview"
- android:scrollbars="none" >
- </ListView>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/detail_recommend_icon"
- android:layout_marginLeft="57dp"
- android:layout_marginRight="@dimen/programa_gridview_left"
- android:layout_marginTop="100dp"
- android:layout_toRightOf="@id/app_wall_listview" >
- <GridView
- android:id="@+id/app_wall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:listSelector="@android:color/transparent"
- android:numColumns="3"
- android:scrollbars="none"
- android:verticalSpacing="@dimen/search_game_left" >
- </GridView>
- </RelativeLayout>
- </RelativeLayout>
其实在做本项目的时候,还有一个逻辑问题,就是当前选中的item放大,之前的item恢复原样,这个逻辑也是在代码里进行了注释和体现,希望大家注意。
关于这行代码,convertView = LayoutInflater.from(context).inflate(R.layout.item_app_wall2, parent, false);如果你不不是很懂的话,建议你看这篇文章,我觉得是很重要 的
http://blog.csdn.net/harryweasley/article/details/46646773(
LayoutInflater.inflate方法解析
)
本项目的完整代码链接:http://download.csdn.net/detail/harryweasley/8886349