听说点赞关注的人,身体健康,万事如意,工作顺利,爱情甜蜜,一夜暴富,升职加薪……最终迎娶白富美!!!
微信公众号:炜煜gzs
题目:🐱安卓开发-基础知识补习2
内容简介:本文通过学习明日学院的视频以及阅读相关大佬的博客,简单的介绍了draw9patch,stateListDrawable,安卓中常用适配器,HashTable,万能适配器,以及ListActivity的相关简单用法,通过几个例子实现了简单的相关知识的实践,使用java代码进行开发,如果有问题的地方请不吝指教,如果对文内内容有不理解的地方,也希望能积极主动的联系博主进行深刻的探讨,以便于更好的让博主记住这篇博文的内容,好让博主在发光发热的道路上越走越远。[手动狗头🐶]努力,奋斗!
标题一:draw9patch图片
.9格式图片:9-patch图片定义——Android SDK提供的解决应用开发过程中和UI相关的制作自适应背景图片的问题(就是一个使用9-patch制作的PNG图篇拉伸之后,失真幅度更小,非常适合开发使用)。实际工作中,会由UI设计师做好,然后开发者直接使用即可;若是开发者想自己平时开发练习使用,也可以学习如何制作9-patch图片。
制作规则:选择连续的画面:如果横向拉伸不会变形,那么横向区域就是横向可缩放区域,纵向同理。
shop patches:显示切片,粉色为可缩放区域,绿色部分为内容显示区,内容不会发生缩放。
保存的时候选择格式为.9.png格式
放入xml文件中,需要注意,文件名不能出现大写字母,必须是小写字母或者是数字。
标题二:StateListDrawable
StateListDrawable:定义在XML文件中的Drawable对象,根据状态,出现不同的图片。状态列表资源。可用不可用,失去获得焦点等事件。
标题三:安卓中常用适配器的用法
适配器
顾名思义,就是把一些数据弄得适当,适合以便于在View上显示,可以看作是界面数据绑定的而一种理解,它所操纵的数据一般都是一些比较复杂的数据,如数组,链表,数据库,集合等。适配器其就像是显示器,把复杂的东西按人可以接受的方式来展现。
那么适配器是怎么处理得到的数据,并把他们展示出来的呢。其实很简单,说白了适配器他就是一个类,在类里面实现了服了的这几个方法。
public int getCount() //得到数据的行数
public Object getITem(int position); //根据position得到某一行的记录
public long getItemID(int position) //得到某一条记录的ID
public View getView(int position,View conversView,ViewGroup parent) //
//getView()方法是最重要的相比于其他几个方法。
//它显示的定义了适配器将要以什么样的方式区显示我们所填写的数据
//在自定义的适配器里面我们通常会给它写个布局文件
常用的适配器主要有:ArrayAdapter,SimpleAdapter,SimpleCursorAdapter,这三个,他们都继承于BaseAdapter,一般对于前两个适配器,他们的数据来源无非就是字符串数组或者集合。
ArrayAdapter的使用
public class Example extends ListActivity {
String[] sex = new String() {"男","女"};//数据源
ArrayAdapter<String> adapter;//数组适配器,用的是泛型
public void onCreate(Bundle SavedInstanceState) {
super.onCreate(SavedInstanceStat);
/*在对适配器初始化的时候,顺便把数据源装载到适配器里,
this.android.R.Layout.Simple_List_Item_1
这句话的意思是将数据源以系统定义好的样式放到适配器里.*/
adapter = new ArrayAdapter<String>(this.android.R.Layout.Simple_List_Item_1, sex); this.setAdapter(adapter);
//这是一个控件类,所以可以直接将适配器绑定到本身对象中。
}
}
SimpleAdapter的使用
ListView list = (ListView) findViewById(R.id.MyListView);
//生成动态数组,并且转载数据
ArrayList<HashMap<String,String>> mylist = newArrayList<HashMap< String, String>>();
for(int i = 0;i<30;i++)
{
HashMap<String, String> map = new HashMap<String, String>();
map.put("ItemTitle", "This is Title.....");
map.put("ItemText", "This is text.....");
mylist.add(map);
}
//生成适配器,数组===》ListItem
SimpleAdapter mSchedule = new SimpleAdapter(this, mylist,//数据来源
R.layout.my_listitem,//ListItem的XML实现
new String[]{"ItemTitle", "ItemText"},//动态数组与ListItem对应的子项
new int[] {R.id.ItemTitle,R.id.ItemText});//ListItem的XML文件里面的两个TextView ID
list.setAdapter(mSchedule);//添加并且显示
SimpleCursorAdapter的使用
SimpleCursorAdapter:一般主要用于数据库,它的数据来源一般都是数据库查询得到的cursor,例子如下:
String uriString = "content://contacts/people/";
Cursor myCursor = managedQuery(Uri.parse(uriString), null, null, null, null);
String[] fromColumns = new String[]{People.NUMBER, People.NAME};
int[] toLayoutIDs = new int[]{R.id.nameTextView, R.id.numberTextView};
SimpleCursorAdapter myAdapter;
myAdapter=new
SimpleCursorAdapter(this,R.layout.simplecursorlayout, myCursor, fromColumns, toLayoutIDs);
//传入当前的上下文、一个layout资源,一个游标和两个数组:一个包含使用的列
//的名字,另一个(相同大小)数组包含View中的资源ID,用于显示相应列的数据值。
myListView.setAdapter(myAdapter);
//我们把一个游标绑定到了ListView上,并使用自定义的layout显示来显示每一个Item。
自定义适配器
当我们需要特定的方式来展示数据,但是目前拥有的适配器又不能完全满足我们的需求,就得自己创建适配器,按需求进行开发。
自定义适配器的步骤
- 定义一个适配器类
- 继承BaseAdapter类或者Adapter类
- 重写方法
public class ImageAdapter extends BaseAdapter {
private Context mcontext;
private Integer[] imageIds ={R.id.imager1};
//构造函数里面有两个参数,一个是数据的来源,另一个是上下文。
public ImageAdapter(Integer[] imgIds, Context c) {
mcontext = c;
imageIds = imgIds;}
//此方法用来判断适配的元素的个数
public int getCount() {
// TODO Auto-generated method stub
return imageIds.length;}
//此方法用来获取单个元素
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;}
//此方法用来获取单个元素的ID
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;}
//主要工作是做在这里,可以自定义布局,在这里我就不多说了
public View getView(int position, View convertView, ViewGroup parent){
// TODO Auto-generated method stub
//此处对布局进行自定以,先创建控件对象,然后进行自定义布局和给控件传入定义的数据源
ImageView imageview = newImageView(mcontext);
imageview.setImageResource(imageIds[position]);
imageview.setLayoutParams(newGallery.LayoutParams(120, 120));
imageview.setScaleType(ImageView.ScaleType.FIT_CENTER);
return imageview;}}
自定义适配器的使用较为常见,此处使用简单的例子做一个简单的介绍,后续使用的时候根据需求进行开发。
ListActivity使用步骤
1,继承自ListActivity,如xxx.extends ListActivity
2,重写onCreate方法(和继承Activity类似),在该方法中需要做三件事
(1),准备数据源
(2),设置适配器
(3),绑定适配器
3,重写点击事件void onListItemClick(ListView i,View v, int position, long id);
@SuppressWarnings 抑制编译器警告注解
@SuppressWarnings(“unchecked”) // 抑制未检查的转化,例如集合没有指定类型的警告
@SuppressWarnings(“unused”) // 抑制未使用的变量的警告
@SuppressWarnings(“resource”) // 抑制与使用Closeable类型资源相关的警告
@SuppressWarnings(“path”) // 抑制在类路径,原文件路径中有不存在的路径的警告
@SuppressWarnings(“deprecation”) // 抑制使用了某些不赞成使用的类和方法的警告
@SuppressWarnings(“fallthrough”) // 抑制switch语句执行到底没有break关键字的警告
@SuppressWarnings(“serial”) // 抑制某类实现Serializable,但是没有定义serialVersionUID,这个需要但是不必须的字段的警告
@SuppressWarnings(“rawtypes”) // 抑制没有传递带有泛型的参数的警告
@SuppressWarnings(“all”) // 抑制全部类型的警告
public class Example extend ListActivity{
public void onCreate(Bundle saceInstanceState){
super.onCreate(saveInstanceState);
//准备数据源,安卓控件需要显示的内容,一般为数组类型
String[] arr = new String[]{"老师","学生","课桌","书本","铅笔","橡皮","粉笔","黑板","凳子","扫帚","簸箕","炉子","窗花","讲台","教鞭","小红花","花瓶"};
//创建设配器对象
ArrayAdapter arrayAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_!,data);
//绑定适配器
setListAdapter(arrayAdapter);
}
//......(类似item的点击事件可以写在后面)
protected void onListItemClick(ListView l,View v,int position,long id){
//......
}
}
常见的显示样式包括以下几种
android.R.layout.simple_list_item_1 一行text ;
android.R.layout.simple_list_item_2 一行title,一行text ;
android.R.layout.simple_list_item_single_choice 单选按钮
android.R.layout.simple_list_item_multiple_choice 多选按钮
android.R.layout.simple_list_item_checked checkbox
也可以使用自定义适配器,继承相应的Adapter即可。
标题四:HashTable详解
HashTable是较为远古的使用Hash算法的容器结构了,现在基本已经被淘汰了,但吸纳成转为使用HashMap,多线程使用ConcurrentHashMap。
HashTable<K,V>也是一种key-value结构,它继承自Dictionary<K,V>,实现了Map<K,V>和loneable以及Serializable接口。HashTable的操作集合和HashMap一致,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁,所以是线程比较安全的,而加锁的结果就是HashTable擦偶做的效率就十分低下。
HashTable与HashMap对比:
(1)线程安全:HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高;HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低;
(2)插入null:HashMap允许有一个键为null,允许多个值为null;但HashTable不允许键或值为null;
(3)容量:HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16;而HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11;
(4)Hash映射:HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗;而HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算;
/*
// HashMap
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 下标index运算
int index = (table.length - 1) & hash(key)
// HashTable
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;*/
(5)扩容机制:HashMap创建一个为原先2倍的数组,然后对原数组进行遍历以及rehash;HashTable扩容将创建一个原长度2倍的数组,再使用头插法将链表进行反序;
(6)结构区别:HashMap是由数组+链表形成,在JDK1.8之后链表长度大于8时转化为红黑树;而HashTable一直都是数组+链表;
(7)继承关系:HashTable继承自Dictionary类;而HashMap继承自AbstractMap类;
(8)迭代器:HashMap是fail-fast(查看之前HashMap相关文章);而HashTable不是。
不建议直接使用HashTable,Oracle官方也会将其废弃,建议在多线程环境下使用ConcurrentHashMap类。
标题五:万能适配器
关于万能适配器的功能目前还没看懂,此处将大佬的博客直接粘贴过来,后面需要的时候再细细品读理解。
-
在安卓开发中,用到ListView和GridView的地方实在是太多了,系统默认给我们提供的适配器(ArrayAdapter,SimpleAdapter)经常不能满足我们的需要,因此我们时常要去继承BaseAdapter类去实现一个自定义的适配器来满足我们的场景需要。
如果你是开发一个简单点的APP还好,可能ListView和GridView的数量不会太多,我们只要去写几个BaseAdapter实现类就可以了。
但如果有一天,你需要开发一个APP里面具有几十个ListView或者GridView的子页面,此时的你该怎么办?每个ListView或者GridView都去写一个适配的Adatper类吗?
当然你如果想做蛮牛不嫌累的话也不是不可以,但如果有办法可以让自己减少很多工作量,避免做重复无意义劳动,何乐而不为呢?
万能适配器思想?
软件设计模式:模板方法模式(有兴趣了解的朋友,可以参考看下我之前写过的博文《软件设计模式之模板方法模式(JAVA)》)
其实解决问题的核心思想很简单,一句话:抽取重复代码!
我们在继承BaseAdapter类时,都需要去实现它里面的抽象方法(
getCount, getItem, getItemId, getView
),其中除了getView这个方法里需要实现的代码不同,其他的都一样。而这个getView方法里,我们考虑到性能的问题,我们经常会引入一个ViewHolder类(关于不清楚ViewHolder的朋友可以看看我之前写过的博文《安卓开发笔记——ListView加载性能优化ViewHolder》),尽可能的去节省资源。
那么解决问题的思路就出来了,我们可以把这个适配器抽取成2部分:
第一部分是解决(getCount, getItem, getItemId)方法里重复代码的问题。
第二部分是分离getView方法里使用到的ViewHolder,把它单独抽取出来成一个独立的类,利用键值对Key=>Value的方法,以控件ID去寻找对应的View对象。
如果你看完以上这些感觉已经云里来雾里去,没关系,接下去我们用代码说话。
传统适配器的实现方式:
View Code
从上面的代码就可以感受到,如果我们去编写多个适配器Adapter的时候,那么我们就势必要去写多个ViewHolder和重复的去写(getCount, getItem, getItemId)方法,而ViewHolder里面常用的控件View也就无非那几种,而那三个方法里(getCount, getItem, getItemId)的代码也是固定不变的,所以重复代码量非常的多,我们应该把它们抽取出来。
万能适配器实现
1、首先我们先来分离这个ViewHolder,其实核心代码并没有改变,只是把传统ViewHolder给做的事情给分离出来罢了。
package com.example.listviewtest; import android.content.Context; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * ViewHolder集合类 * @author Balla_兔子 */ public class CommonViewHolder { private SparseArray<View> sparseArray; private View convertView; private int position; // 构造方法,完成传统Adapter里的创建convertView对象 public CommonViewHolder(Context context, View convertView, int layoutId, ViewGroup parent, int position) { this.position = position; this.sparseArray = new SparseArray<View>(); this.convertView = LayoutInflater.from(context).inflate(layoutId, parent, false); this.convertView.setTag(this); } // 入口方法,完成传统Adapter里面实例化ViewHolder对象工作 public static CommonViewHolder getCommonViewHolder(Context context, View convertView, int layoutId, ViewGroup parent, int position) { if (convertView == null) { return new CommonViewHolder(context, convertView, layoutId, parent, position); } else { CommonViewHolder commonViewHolder = (CommonViewHolder) convertView.getTag(); //特别需要注意的一点,由于ListView的复用,比如屏幕只显示5个Item,那么当下拉到第6个时会复用第1个的Item,所以这边需要更新position commonViewHolder.position = position; return commonViewHolder; }} //根据控件Id获取对应View对象 public <T extends View> T getView(int viewId) { View view = sparseArray.get(viewId); if (view == null) { view = convertView.findViewById(viewId); sparseArray.put(viewId, view); } return (T) view; } //用于返回设置好的ConvertView对象 public View getConvertView(){ return convertView; } }
这里我们提供了一个入口方法getCommonViewHolder来得到一个ViewHolder的实例对象,若实例不存在,我们去创建并设置Tag保存,这点和先前的ViewHolder所做的事情是一样的。
由于所有的控件都是View的子类,这里提供了一个getView来获取各控件的对象,在我们需要使用的时候强转成我们所需要的控件类型就可以了,这里提供了一个类似Map的集合SparseArray,这个类和Map一样是利用Key=>Value来存取对象的,不同的是这里的Key是整型变量。
下面是SpareseArray源码中对其的介绍:
SparseArray是Android为<Integer,Object>类型的HashMap专门写的类,目的是为了提供效率,其核心算法是折半查找,其用法和Map无两异。
2、再来分离下BaseAdapter,除getView这个方法会有一些不同,其他的代码其实每次书写都是一样的,我们可以自己写一个抽象类把它们都给实现了,只留getView最关键核心的代码部分给用户实现。由于方法操作,我们这里利用泛型
package com.example.listviewtest; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; /** * 通用适配器Adapter写法 * @author Balla_兔子 * @param <T> */ public abstract class CommonAdapter<T> extends BaseAdapter { //为了使得子类可以访问,这里修改包访问级别 protected Context context; protected LayoutInflater layoutInflater; protected List<T> data; protected int layoutId; public CommonAdapter(Context context, List<T> data, int layoutId) { this.context = context; this.layoutInflater = LayoutInflater.from(context); this.data = data; this.layoutId = layoutId; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { //获取ViewHolder对象 CommonViewHolder myViewHolder = new CommonViewHolder(context, convertView, layoutId, parent, position); //需要用户复写的方法,设置所对于的View所对应的数据 setConverView(myViewHolder,data.get(position)); return myViewHolder.getConvertView(); } //用户需要实现的方法 public abstract void setConverView(CommonViewHolder myViewHolder, T t); }
完成上面两部分的分离后,我们看看现在的适配器代码编程什么样子
package com.example.listviewtest; import java.util.List; import android.content.Context; import android.widget.ImageView; import android.widget.TextView; /** * 万能适配器Adapter写法 * @author Balla_兔子 */ public class MyAdapter extends CommonAdapter<User> { public MyAdapter(Context context, List<User> data, int layoutId) { super(context, data, layoutId); } @Override public void setConverView(CommonViewHolder myViewHolder, User user) { ((ImageView) myViewHolder.getView(R.id.iv_image)).setImageResource(R.drawable.ic_launcher); ((TextView) myViewHolder.getView(R.id.tv_name)).setText(user.getName()); ((TextView) myViewHolder.getView(R.id.tv_phone)).setText(user.getPhone()); } }
很明显,代码量减少了近2/3,而且是一劳永逸,CommonAdapter和CommonViewHolder再也不需要变动了,需要什么我们往里面直接加就可以了,这样让我们可以更为专注的去实现核心代码。当然还可以更简化一点,把这些ViewHolder.getView和setText,setImage等方法再一次封装,变成只传递控件Id和对应数据就够了,这样一来我们连类都不需要写了,直接用new对象去写个内部类实现就可以了。
附上主MainActivity代码:
View Code
就像这样,以后如果需要使用适配器Adapter就不需要再去继承BaseAdapter了,直接继承CommonAdapter配合CommonViewHolder就可以了。
内容总结::本文通过学习明日学院的视频以及阅读相关大佬的博客,简单的介绍了draw9patch,stateListDrawable,安卓中常用适配器,HashTable,万能适配器,以及ListActivity的相关简单用法,通过几个例子实现了简单的相关知识的实践,如果还有您觉得没介绍的地方,请用力踢一脚,好让摸鱼的博主积极主动的去认识错误并及时改正,在发光发热的道路上越走越远……如果觉得本篇文章介绍的不错,也希望能得到您的一件三连,实不相瞒,三连确实有点费劲,我自己也很少给,或许你可以试试两连,这个还不错,我经常这样做。好了好了,这篇文章就扯到这里,再多怕被嫉妒,谢谢你这么认真的读完。再加一句,如果有好看的运营小姐姐,也不要吝啬把微信推给我,万一您推荐了那个真命女子,博主后大半辈子感谢你大半辈子。
表情网站:🎁 Emoji cheat sheet for GitHub, Basecamp, Slack & more (webfx.com)