转载请注明出处,谢谢: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