错误调试,由下至上,找CausedBy(然后并没有找到) 里面出现的java异常,在这个异常下面找自己的类中出现的问题,双击可以打开对应的位置
在上一篇笔记中,引入了ListView控件
但是在使用的时候,ListView控件里拖动太快就容易导致内存溢出(条目足够多的情况下)不断拖动,就会不断创建对象,然而垃圾回收器这个时候如果还没有进行回收的话,就一样会导致程序内存溢出,然后崩溃.
ListView中getView方法的四个参数
Int Position 当前View控件的条目数
View covertView 一个用来复用的View
编写代码的时候,就可以通过复用View来避免不断创建对象,这样就不会内存溢出了,而是一直在使用被回收的View
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv =null;
if(convertView==null){
tv = new TextView(MainActivity.this);
}else{
tv = (TextView) convertView;
}
tv.setText(students.get(position).toString());
return tv;
}
2,采用打气筒创建view对象
2.1原因:原来的样式不好看,要更好看的样式.如果直接通过代码创建一个控件,再创建子控件,设置背景颜色,图片,文字等等,这样的话比较麻烦(其实就是懒,个人感觉挺好创建的,不过对代码要更熟悉一点),所以google提供了一个api来引用外部的xml样式文件来达到这一效果.
创建的xml文件相当于一个气球,使用打气筒View.inflate(context,resource(资源文件),root(代表气球所处的位置,通常为null))(把一个 xml文件转换成View对象)就可以用了.
注意:在匿名内部类中使用findViewById()的时候,使用的可能是MainActivity里面的方法(它获取到的id在它对应的xml文件中),而不是前面使用inlate获得的view对象对应的xml文件,所以获得的id对象可能不一样
inflate转换的view 对象,底层也是用的pull解析,所有的布局文件都是通过转换成view对象,add到页面之上的.
2.2 因为这个listView很重要,所以重复一遍步骤
①,在布局xml文件中声明listView控件
②,在java 代码中找到ListView并且创建适配器
③,getCount()来确定listView中条目的数量
④,getView()返回某个位置显示的view对象
⑤View.inflate()打气筒可以把xml文件转换成view去显示
android:layout_marginLeft属性,距离左边一个控件的距离
2.3 其它事项:①添加一个图标显示删除按钮
②删除之后会跳到第0条记录,因为每次调用显示功能都相当于创建了一个新的适配器,所以会回到最开始的位置;
解决方案:
不能使用匿名内部类去做显示功能,要单独提取出来,创建一个类去实现BaseAdapter类)
//通知数据适配器更新数据,而不是new出来新的适配器(创建一个适配器,判断它是否为空)
使用adapter.notifyDataSetChanged(),底层会先调用getCount方法,
ListView.setSelection(条目数)设置对应的条目显示位置
③通过在onCreate方法中直接定义条目的点击删除方法
Listview.setOnItemClickListener() 条目点击事件(是整个条目,而不是单个控件)
Listview.setOnItemLongClickListenter()长按条目触发的事件
mov平滑啥的,没记住
④封装ListView中的控件,面向Holder编程(未封装前每次创建convertView都会加载一次控件,通过封装控件来提高效率,节约控件)(找时间做)
convertView.setTag(obj)可以储存对象(这个对象里封装控件)
getTag()可以获得对象,(记得要强转,但是多个对象被储存了该怎么取出来?怎么判断)
//作用,当第一次使用的时候,就把对象创建出来,当convertView被重复使用的时候,就不用重复创建子控件对象
3,消息框
3.1 创建一个对话框,谷歌采用了工厂类的设计模式
AlertDialog.Builder builder = new Builder(this);
//设置消息框的名称
builder.setTitle(“警告”);
//提示文本
builder.setMessage(“XXXXXX”);
//设置确定取消按钮
builder.setPositiveButton(“确定”,new OnclickListener())
//取消对话框
builder.setNegativeButton(“取消”,点击事件)
//创建一个警告的对话框
AlertDialog dialog = Builder.create();
Dialog.show();
3.2//单选对话框
AlertDialog.Builder builder = new Builder(this);
builder.setTitle(“文本”);
Final String items=(x,x,x);
builder.setSingleChoiceItems(items,默认性别(索引,-1什么都不选),点击事件);
//取消对话框
builder.setNegativeButton(“取消”,null);
Builder.show(); (AlertDialog dialog = Builder.create();
Dialog.show();
)这两个方法的封装
3.3//多选对话框
AlertDialog.Builder builder = new Builder(this);
//设置对话框的名称
builder.setTitle(“文本”);
String [] items = {x,x,x};
Boolean [] checked={x,x,x};
builder.setMultiChoiceItems(items,默认选项(通过checked布尔类型数组来确定),点击事件);
点击事件中重写方法有一个isChecked变量,用来传入是否被点击,记得要给对应的选项状态重新赋值,不然一直都是初始状态
3.4//取消对话框
builder.setNegativeButton(“取消”,null);
Builder.show();
3.5//特殊对话框-等待对话框
ProgressDialog pd =new ProgressDialog(this);
pd.setTitle(“提醒”);
pd.setMessage( “正在加载数据....稍后”);
Pd.show();
//关闭方法
Ps.dismiss();
3.6//进度条对话框
ProgressDialog pd =new ProgressDialog(this);
//设置进度显示方式,默认旋转的弧线
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//设置长度
pt.setMAX(100);
pd.setTitle(“提醒”);
pd.setMessage( “正在加载数据....稍后”);
Pd.show();
//设置进度条进度
pd.setProgress(数字,不大于长度);
//关闭方法
Ps.dismiss();
注意builder设置单击事件的时候注意导包
ListView属性:fastScrollEnable 快速进度条,可以直接拖动到末尾
额外:姓名去重,遍历已经存在的姓名,如果姓名已经存在,就提示是否覆盖(消息框)
4,谷歌封装的SQL语句的API(谷歌封装的,并不是原生的,所以要注意,不要把sql语句给荒废了)
SQLiteDatabase db = helper.getWritableDatabase();
4.1
db.insert(表名,null,ContentValues);(本方法底层就是SQL语句的添加操作,并且带有返回值,返回添加到的行数,返回为-1就代表没有添加成功)
第二个参数,当第三个为空的时候,第二个参数就为列名称,否则为null
第三个参数底层是一个HashMap集合,键代表列名称,值代表具体的数据
4.2
db.delete(表格,where条件,选择条件的参数参数),返回代表删除的数量
4.3
db.update(表名,修改的新的数值(ContentValues),where条件,选择条件的参数,String数组);返回成功的数量,如果为0就代表没有修改成功
4.4
Cursro cursor = db.query(表名,获取的列数组,选择条件,选择条件对应的参数,是否分组,分组条件(Having条件),是否排序);
5.数据库的事务
5.1事务:保证一组操作要么同时成功(并不代表同时执行,但是保证结果同时出现),要么同时失败(例如银行转账,A向B转账1000.A的账户就会扣除1000,B的增加1000,当这中间放生故障时,B的收账失败,A的转账也要失败)
5.2数据库事务逻辑
//使用场景:大数据量添加(因为通过临时文件像数据库添加数据是通过C语言实现的,效率更高,速度更快)
保证操作结果的同步性
db.beginTransaction();//开启事务
Try{
代码逻辑//这些逻辑先存放在临时文件中,之后遇到事务执行成功的逻辑,就会一次性全部添加到数据库中.
db.setTransactionSuccessful();//设置事务执行成功
}finally{
db.endTransaction();//保证事务有开启有关闭,如果没有关闭的话,数据库会一直被锁定,其它操作就不能执行(或者说一直等待)
}
Db.close;
6,数据适配器(ListView中提到过)
常用的数据适配器(都继承了BaseAdapter适配器):
6.1 ArrayAdapter 数组适配器
//适用于纯文本
String arr = new String[]{x.x.x.x.x};
listview.setAdapter(new ArrayAdapter<String>(this,R.layout.对应的文件),String数组);
//可以在ListView中显示一组条目(然后可以设置条目的点击事件,跳转到别的逻辑中)
6.2 SimpleAdapter(上下文,参数二,布局文件,参数四,参数五)
//使用场景,用于带图片条目的实现
参数二:list<extends Map<x,x>> data = new ArrayList<Map<x,x>>();
//一个泛型为Map集合的List集合
每一个Map集合,可以用来储存一个图标
Map1.put(“icon”,id);
Map1.put(“name”,”名字”);
最后记得添加到List集合中
后面两个参数是映射关系,用来找到List集合中的数据对应的样式
参数四: new String{“icon”,”name”}
参数五:new int{R.id.v,R.id.v}
注意!使用SimpleAdapter 创建的list,是不能通过notifyDataSetChanged 来更新的,因为SimpleAdapter 主要是用来创建静态的数据的列表,如果要实现动态更新数据,需要自己定义一个基于BaseAdapter的adapter,然后通过notifyDataSetChanged 来更新list。
7,Android下的帧动画(一共一三种,帧动画,twee(view)动画,属性动画)
7.1 步骤
①,创建一个drawable文件夹用来实现动画,复制图片
②,创建一个 xml文件 andimation-list类型
③,创建item条目,对应复制的图片,设置闪动的频率
⑤,创建一个图片控件,并找到它
⑥,设置背景图片资源 img.setBackgroundResource( xml文件)(布局文件中配置属性也可以)
⑦,转换成动画资源 AnimationDrawable anim = (AnimationDrawable)img.getBackground();
⑧,开始播放 andim.start();
注意,如果设置src属性(会优化处理),会在BackgroundResouce(不会)之上
//具体代码
XML文件中
<!-- Animation frames are wheel0.png -- wheel5.png files inside the
res/drawable/ folder -->
<animation-list android:id="@+id/selected" android:oneshot="false">
<!--duration 闪动的频率-->
<item android:drawable="@drawable/wheel0" android:duration="50" />
<item android:drawable="@drawable/wheel1" android:duration="50" />
<item android:drawable="@drawable/wheel2" android:duration="50" />
<item android:drawable="@drawable/wheel3" android:duration="50" />
<item android:drawable="@drawable/wheel4" android:duration="50" />
<item android:drawable="@drawable/wheel5" android:duration="50" />
</animation-list>
//代码
ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
//设置背景图片资源
img.setBackgroundResource(R.drawable.spin_animation);
//转换成动画资源
AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();
//开始播放动画
frameAnimation.start();
8,应用程序的国际化(非常非常的简单)
I18n(国际化18个单词的简写,可用于创建包名)
8.1,创建一个values-en文件夹,代表英文环境下的app字符串显示(不需要去获取手机使用的语言环境,Android会根据不同的语言环境获取对应的values-xx下的String.xml文件)
文字的国际化
如果不记得语言简写(百度)
8.2使用这种方法实现的国际化,在布局文件中创建控件的文本属性时使用@String/文本值,就可以在不同语言环境下显示不同的文本.
在代码中需要使用字符串的地方通过getResources().getString(R.String.XXX)获取字符串.
图片的国际化:把对应国家的图片放在对应的values-XX文件夹下,图片名一致(不然设置图片的时候还要进行判断,简化一下更方便)
8.3把字符串抽取出来的好处:方便后期维护,便于查找.
9.样式和主题(样式的配置都可以通过代码实现)
注意 定义自定义样式需要定义item属性,名称是对应的背景,文字名,文本值是对应的值
<item name=”android:layout_width”>match_parent</item>
9.1
样式的定义:在styles.xml(values文件夹下)定义属性style标签,把属性定义在里面,记得给style设置名字(才能在控件中使用);
样式的使用:在需要使用的控件中定义style属性,指向定义的样式(R.style.xxx)
作用范围:在控件上style
9.2
主题和样式没有绝对的区别,作用范围不一样
作用范围: 作用在Activity上,theme属性
定义一个主题,在styles.xml中,定义style标签,name属性 在清单中查找的标识
在清单文件下application定义theme属性指向对应的style标签
9.3 样式的继承
创建一个新的style属性,定义name属性,定义parent=”@style/XXX”>
可以自定义属性替换父样式的属性,其它属性可以继承
已经被废弃的写法(需要了解知道)
style属性中写 name =”text_content_style_sub” 会继承text_content_style的样式.
附上楼主自己写的学生MainActivity页面练习代码,数据库的crud比较简单,就不贴出来了
package com.zzx.student; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.RadioGroup; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; import com.zzx.student.db.dao.MyDBImp; import com.zzx.student.domain.Student; public class MainActivity extends Activity { //拿到需要的控件 private ImageView iv_title; private EditText et_name; private RadioGroup rg_sex; private Button bt_sub; private ListView lv_show; private MyDBImp mdb ; private List<Student> students; private MyAdapter ma; private List< Map<String, Object>> list; private Map<String,Object> map; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化控件 initView(); //设置帧动画 showAnimation(); //设置单击事件 setOnClick(); //显示事件 showAll(); } /** * 这里用来显示所有学生信息 */ private void showAll() { students = mdb.findall(); list = new LinkedList<Map<String,Object>>(); //遍历students集合 for(Student s :students){ map = new HashMap<String, Object>(); String sex = s.getSex(); int a ; //判断性别的图片 if("男".equals(sex)){ a = R.drawable.man; }else{ a = R.drawable.woman; } //添加信息 map.put("sex", a); map.put("name", s.getName()); map.put("delete", R.drawable.no_signal_96dp); //添加进集合 list.add(map); } //把自定义适配器抽取成私有的,方便优化代码 ma = new MyAdapter(MainActivity.this, list, R.layout.simpleadapter, new String[]{"sex","name","delete"}, new int[]{R.id.show_sex,R.id.show_name,R.id.d_m}); lv_show.setAdapter(ma); lv_show.setSelection(10); } /** * 这是一个自定义适配器类 * @author msi * */ class MyAdapter extends SimpleAdapter{ public MyAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); } } /** * 这个方法用来设置listView的删除事件 */ public void deleteMe(View view){ //设置警告信息 AlertDialog.Builder adb = new Builder(MainActivity.this); adb.setTitle("警告"); //找到父控件,通过父控件找到子控件,获取要删除的信息 View vp = (View) view.getParent(); final String name = ((TextView)vp.findViewById(R.id.show_name)).getText().toString(); adb.setMessage("您是否要删除" + name + "的信息"); adb.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "删除成功", 0).show(); mdb.delete(name); //刷新一下界面 showAll(); } }); //取消操作 adb.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "取消成功", 0).show(); } }); adb.show(); } /** * //设置保存信息事件 */ private void setOnClick() { bt_sub.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String name = et_name.getText().toString().trim(); //判断姓名是否为空 if(TextUtils.isEmpty(name)){ Toast.makeText(MainActivity.this, "姓名不能为空", 0).show(); return; } //获取性别 String sex = "男"; switch (rg_sex.getCheckedRadioButtonId()) { case R.id.rb_man: sex = "男"; break ; case R.id.rb_woman: sex = "女"; break; } Toast.makeText(MainActivity.this, "添加成功", 0).show(); //添加进数据库, mdb.add(name, sex); showAll(); } }); } /** * 这个方法用来做标题动画效果 */ private void showAnimation() { iv_title.setBackgroundResource(R.drawable.myantimation); //转换成动画资源 AnimationDrawable anim = (AnimationDrawable) iv_title.getBackground(); //开始播放 anim.start(); } /** * 初始化控件 */ private void initView() { iv_title = (ImageView) findViewById(R.id.imgtitle); et_name = (EditText) findViewById(R.id.ed_name); rg_sex = (RadioGroup) findViewById(R.id.rg_sex); bt_sub = (Button) findViewById(R.id.submit); lv_show = (ListView) findViewById(R.id.lv_showall); mdb = new MyDBImp(MainActivity.this); } }
xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/imgtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText android:id="@+id/ed_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="@string/studentname" android:textSize="22sp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/selectsex" android:textSize="22sp" /> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/rg_sex" android:orientation="horizontal" > <RadioButton android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="@string/rb_man" android:id="@+id/rb_man" /> <RadioButton android:id="@+id/rb_woman" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="@string/rb_woman" /> </RadioGroup> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/submit" android:text="@string/bt_sb" /> <ListView android:fastScrollEnabled="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/lv_showall" > </ListView> </LinearLayout>
适配器布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <ImageView android:id="@+id/show_sex" android:layout_width="30dp" android:layout_height="30dp" /> <TextView android:id="@+id/show_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/show_sex" android:textSize="30dp" /> <ImageView android:layout_alignParentRight="true" android:id="@+id/d_m" android:layout_width="30dp" android:layout_height="30dp" android:src="@drawable/no_signal_96dp" android:onClick="deleteMe" /> </RelativeLayout>
图标略