Android GridView控件自定义

简介:

虽然Android已自带了GridView,但是,却不够灵活,同时也不能自由添加控件,因此,本人通过需要进一步封装,来实现Android自定义GridView控件,达到自己需要的效果。

我们看一下最终所需要的效果图:

说明:

上图:这里先省去分页,只讲:Android GridView 控件实现自定义。

按照上面的图例需求,大致上可以把Android GridView 画成如下的方式:

思路如下:

   默认将我们的组合控件设置为Orientation 是VERTICAL。 首先一行五个,那么一行以一个Orientation 为HORIZONTAL 的线性布局包起来。然后在一行结束后,将Orientation 的线性布局添加进组合控件里面来,不足五个则按需添加进来。

  实现这一效果我们需要两个类,一个类用来表示Android GridView 的行,这里我们起名为TableRow,代码如下:

public class TableRow {
private TableCell[] cell;

public TableRow(TableCell[] cell) {
this.cell = cell;
}

public int getSize() {
return cell.length;
}

public TableCell getCellValue( int index) {
if (index >= getSize()) {
return null;
}
return cell[index];
}

public int getCellCount() {

return cell.length;

}

public int getLastCellCount() {
return lastRowCount;
}
}

  另外一个类用来表示GridView 每行的列个,这里我们取名为TableCell,代码如下:

static public class TableCell {
private Object value;

public TableCell(Object value) {
this.value = value;
}

public Object getValue() {
return value;
}
}

  并且我们还需要为GridView 设置一个外部可添加数据的方法,代码如下:

public void setAdapter(AppsAdapter appsAdapter) {
this.adapter = appsAdapter;
this.setOrientation(LinearLayout.VERTICAL);
bindView();
}

其中,AppsAdapter 是一个自定义的BaseAdapter ,代码很简单,这里就不列出来了。关键的还是要看bindView ,这个方法是本篇GridView 显示数据的核心方法,代码如下:

void bindView() {
removeAllViews();
int count = adapter.getCount();
TableCell[] cell = null;
int j = 0;
LinearLayout layout;
tableRowsList = new ArrayList<HashMap<String, Object>>();
for ( int i = 0; i < count; i++) {
j++;
final int position = i;
if (j > getColumnCount() || i == 0) {
cell = new TableCell[getColumnCount()];
}

final View view = adapter.getView(i, null, null);

view.setOnTouchListener( new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
unCheckPressed();
checkRowID = - 1;
checkColumnID = - 1;
if (onItemClickEvent != null) {

onItemClickEvent.onItemClick(position, event, view);
}
return false;
}
});

view.setOnLongClickListener( new OnLongClickListener() {

@Override
public boolean onLongClick(View v) {
if (onLongPress != null) {
onLongPress.onLongPress(v);
}
return true;
}
});
cell[j - 1] = new TableCell(view);
if (j == getColumnCount()) {
lastRowCount = j;
j = 0;
HashMap<String, Object> map = new HashMap<String, Object>();
TableRow tr = new TableRow(cell);
map.put( " tableRow ", tr);
tableRowsList.add(map);
layout = new LinearLayout(getContext());
addLayout(layout, cell, tr.getSize(), tr);

} else if (i >= count - 1 && j > 0) {
lastRowCount = j;
HashMap<String, Object> map = new HashMap<String, Object>();
TableRow tr = new TableRow(cell);
map.put( " tableRow ", tr);
tableRowsList.add(map);
layout = new LinearLayout(getContext());
addLayout(layout, cell, j, tr);
}

}

}

getColumnCount()是一个属性,表示可以从xml或者从代码动态改变GridView 每列显示的个数,属性点的代码为如下:

public gridViewExt(Context context, AttributeSet attrs) {
super(context, attrs);
int resouceID = - 1;
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.GridViewExt);
int N = typedArray.getIndexCount();
for ( int i = 0; i < N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.GridViewExt_ColumnCount:
resouceID = typedArray.getInt(
R.styleable.GridViewExt_ColumnCount, 0);
setColumnCount(resouceID);
break;

}
}
typedArray.recycle();
}

当然,你必须在res 创建属性xml ,这里不多讲,可以去我博客看看如何为 View 添加属性

还有,还必须实现它的支持键盘的上下左右的焦点,下面的代码将会提供该功能,但还必须配合Activity 的操作,等下文再讲述。效果是这样的:

全部源码为:

package com.yaomei.widget;

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

import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.yaomei.activity.adapter.AppsAdapter;
import com.yaomei.activity.info.R;

public class gridViewExt extends LinearLayout {
public List<HashMap<String, Object>> tableRowsList;
private List<HashMap<String, Object>> app = new ArrayList<HashMap<String, Object>>();
private AppsAdapter adapter;

onItemClickListener onItemClickEvent;
onLongPressExt onLongPress;
int checkRowID = - 1; // 选中行的下标
int checkColumnID = - 1; // 选中列的下标
int lastRowCount = - 1; // 最后一行的总数
private int ColumnCount; // 每列的总数

public void setColumnCount( int count) {
this.ColumnCount = count;
}

public int getColumnCount() {
return ColumnCount;
}

public interface onItemClickListener {
public boolean onItemClick( int position, MotionEvent event, View view);
}

public interface onLongPressExt {
public boolean onLongPress(View view);
}

public gridViewExt(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}

public gridViewExt(Context context, AttributeSet attrs) {
super(context, attrs);
int resouceID = - 1;
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.GridViewExt);
int N = typedArray.getIndexCount();
for ( int i = 0; i < N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.GridViewExt_ColumnCount:
resouceID = typedArray.getInt(
R.styleable.GridViewExt_ColumnCount, 0);
setColumnCount(resouceID);
break;

}
}
typedArray.recycle();
}

public void setOnItemClickListener(onItemClickListener click) {
this.onItemClickEvent = click;
}

public void setOnLongPressListener(onLongPressExt longPress) {
this.onLongPress = longPress;
}

public void NotifyDataChange() {
removeAllViews();
}

void bindView() {
removeAllViews();
int count = adapter.getCount();
TableCell[] cell = null;
int j = 0;
LinearLayout layout;
tableRowsList = new ArrayList<HashMap<String, Object>>();
for ( int i = 0; i < count; i++) {
j++;
final int position = i;
if (j > getColumnCount() || i == 0) {
cell = new TableCell[getColumnCount()];
}

final View view = adapter.getView(i, null, null);

view.setOnTouchListener( new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
unCheckPressed();
checkRowID = - 1;
checkColumnID = - 1;
if (onItemClickEvent != null) {

onItemClickEvent.onItemClick(position, event, view);
}
return false;
}
});

view.setOnLongClickListener( new OnLongClickListener() {

@Override
public boolean onLongClick(View v) {
if (onLongPress != null) {
onLongPress.onLongPress(v);
}
return true;
}
});
cell[j - 1] = new TableCell(view);
if (j == getColumnCount()) {
lastRowCount = j;
j = 0;
HashMap<String, Object> map = new HashMap<String, Object>();
TableRow tr = new TableRow(cell);
map.put( " tableRow ", tr);
tableRowsList.add(map);
layout = new LinearLayout(getContext());
addLayout(layout, cell, tr.getSize(), tr);

} else if (i >= count - 1 && j > 0) {
lastRowCount = j;
HashMap<String, Object> map = new HashMap<String, Object>();
TableRow tr = new TableRow(cell);
map.put( " tableRow ", tr);
tableRowsList.add(map);
layout = new LinearLayout(getContext());
addLayout(layout, cell, j, tr);
}

}

}

private void addLayout(LinearLayout layout, TableCell[] cell, int size,
TableRow tr) {

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 130,
110);
layout.setGravity(Gravity.LEFT);

layout.setOrientation(LinearLayout.HORIZONTAL);
for ( int k = 0; k < size; k++) {
View remoteView = (View) tr.getCellValue(k).getValue();
layout.addView(remoteView, k, params);
}
LinearLayout.LayoutParams firstParams = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
firstParams.leftMargin = 60;
addView(layout, firstParams);
}

public void setAdapter(AppsAdapter appsAdapter) {
this.adapter = appsAdapter;
this.setOrientation(LinearLayout.VERTICAL);
bindView();
}

public void checkPressed( int tableRowId, int tableRowColumnId) {
ViewGroup view = (ViewGroup) this.getChildAt(tableRowId);

checkColumnID = tableRowColumnId;
checkRowID = tableRowId;
changeImageState(view.getChildAt(tableRowColumnId), app);

}

public void onClick( int tableRowId, int tableRowColumnId, Context context) {
LinearLayout view = (LinearLayout) ((ViewGroup) this
.getChildAt(tableRowId)).getChildAt(tableRowColumnId);

TextView tv = (TextView) view.findViewById(R.id.folder);
final String[] name = tv.getText().toString().split( " - ");
Intent intent = null;
if (name[ 0].toString().equals( " com.android.contacts ")) {
if (name[ 1].toString().equals(
" com.android.contacts.DialtactsActivity ")) {
intent = new Intent(Intent.ACTION_DIAL);
}
if (name[ 1].toString().equals(
" com.android.contacts.DialtactsContactsEntryActivity ")) {
intent = new Intent(Intent.ACTION_CALL_BUTTON);
}
} else {
intent = getContext().getPackageManager()
.getLaunchIntentForPackage(name[ 0].toString());
}
context.startActivity(intent);

}

/* *
* 改变图片状态
*
* @param v
* @param list
*/
private void changeImageState(View v, List<HashMap<String, Object>> list) {
int size = list.size();
for ( int i = 0; i < size; i++) {
View view = (View) list. get(i). get( " touch ");
view.setPressed( false);
list.remove(i);
}
v.setPressed( true);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put( " touch ", v);
list.add(map);

}

public void unCheckPressed() {
if (checkColumnID != - 1 && checkRowID != - 1) {
ViewGroup view = (ViewGroup) this.getChildAt(checkRowID);
view.getChildAt(checkColumnID).setPressed( false);

}
}

public class TableRow {
private TableCell[] cell;

public TableRow(TableCell[] cell) {
this.cell = cell;
}

public int getSize() {
return cell.length;
}

public TableCell getCellValue( int index) {
if (index >= getSize()) {
return null;
}
return cell[index];
}

public int getCellCount() {

return cell.length;

}

public int getLastCellCount() {
return lastRowCount;
}
}

static public class TableCell {
private Object value;

public TableCell(Object value) {
this.value = value;
}

public Object getValue() {
return value;
}
}

}

每行显示的LAYOUT文件:

< LinearLayout android:orientation ="vertical"
android:background
="@drawable/lessbtn" android:gravity ="center"
android:layout_width
="fill_parent" android:id ="@+id/grid_layout"
android:layout_height
="fill_parent" xmlns:android ="http://schemas.android.com/apk/res/android" >

< ImageView android:id ="@+id/btn_appicon"
android:layout_width
="55dip" android:layout_height ="55dip" ></ ImageView >
< TextView android:id ="@+id/tv_name" android:layout_width ="wrap_content"
android:textColor
="#030303" android:layout_height ="wrap_content" ></ TextView >
< TextView android:id ="@+id/folder" android:layout_width ="wrap_content"
android:visibility
="invisible" android:layout_height ="wrap_content" ></ TextView >

</ LinearLayout >

完成这一系列的编写后,你就可以在xml直接写或者在JAVA文件里面new 出来,但注意要设置它每列显示的个数。

如何,看到这里,是不是觉得自定义的GridView很强大呢?大也自己DIY一下Android GridView控件自定义吧。

相关文章
|
5月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
448 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
5月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
|
5月前
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
139 0
|
5月前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
|
5月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
225 65
Android自定义view之网易云推荐歌单界面
|
5月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
552 84
|
5月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
113 3
|
5月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
Android自定义view之围棋动画(化繁为简)
|
5月前
|
Java Android开发 开发者
Android自定义view之围棋动画
本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。
104 0
|
5月前
|
传感器 Android开发 开发者
Android自定义view之3D正方体
本文介绍了如何通过手势滑动操作实现3D正方体的旋转效果,基于Android自定义View中的GLSurfaceView。相较于使用传感器控制,本文改用事件分发机制(onTouchEvent)处理用户手势输入,调整3D正方体的角度。代码中详细展示了TouchSurfaceView的实现,包括触控逻辑、OpenGL ES绘制3D正方体的核心过程,以及生命周期管理。适合对Android 3D图形开发感兴趣的开发者学习参考。

热门文章

最新文章