使用UISCROLLVIEW实现图片预览

作者:新葡京简介
let screenBounds = UIScreen.mainScreen().boundstypealias CallBack = (a:Int,b:Int)->Int//可循环利用的ScrollViewclass LooperPhoto: UIScrollView,UIScrollViewDelegate{ // 缓存的图片控件 var cacheImages = NSMutableArray() // 活动的图片控件 var actionImages = NSMutableArray()// var photos:[JSON]? private var dataSources:[AnyObject]? var imageType = ImageType.Web var pageCount = 0{ didSet{ if pageCount>oldValue{ self.contentSize = CGSizeMake(CGFloat(pageCount)*CGRectGetWidth(self.bounds), 0) } } } override init(frame: CGRect) { super.init(frame: frame) initSetting() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func awakeFromNib() { initSetting() } private func initSetting(){ self.showsHorizontalScrollIndicator = false self.showsVerticalScrollIndicator = false self.pagingEnabled = true self.delegate = self } func setCurrentPage{ self.setContentOffset(CGPointMake(CGFloat*screenBounds.width, 0), animated: false) } func setImageDataSource(dataSouce:[AnyObject],imageType:ImageType){ self.pageCount = dataSouce.count self.imageType = imageType self.dataSources = dataSouce showImageAtIndex } func showImageAtIndex(index:Int){ var imageView:UIImageView? if (cacheImages.count == 0){ imageView = getImageView }else{ imageView = cacheImages.firstObject as? UIImageView imageView?.frame.origin.x = CGFloat*self.bounds.width cacheImages.removeObjectAtIndex } setImageToImageView(index,imageView: imageView!) imageView!.tag = index actionImages.addObject(imageView!) self.addSubview(imageView!) } private func setImageToImageView(index:Int,imageView:UIImageView){ let data:AnyObject = dataSources![index] var image:UIImage? switch imageType{ case .Local: let alAsset = data as! ALAsset image = UIImage(CGImage: alAsset.defaultRepresentation().fullScreenImage().takeUnretainedValue case .Web: let imageUrl = data as! String imageView.sd_setImageWithURL(NSURL(string: imageUrl)) case .Resource: let resourceName = data as! String image = UIImage(named: resourceName) } if (nil != image){ imageView.image = image! } } func getImageView(index:Int)->UIImageView{ let width = CGRectGetWidth(self.bounds) let imageView = UIImageView() var frame = self.bounds frame.origin.x = CGFloat*width imageView.frame = frame imageView.contentMode = UIViewContentMode.ScaleAspectFit return imageView } //总共就2个image控件 func scrollViewDidScroll(scrollView: UIScrollView) { let scrollViewBounds = scrollView.bounds let minX = CGRectGetMinX(scrollViewBounds) let maxX = CGRectGetMaxX(scrollViewBounds) var firstIndex = Int(minX/scrollViewBounds.width) var lastIndex = Int(maxX/scrollViewBounds.width) if firstIndex<0{ firstIndex = 0 } if lastIndex >= pageCount{ lastIndex = pageCount-1 } var tempImagesArrary = [UIImageView]() for imageView in actionImages{ let index = imageView.tag if index<firstIndex || index>lastIndex{ imageView.removeFromSuperview() tempImagesArrary.append(imageView as! UIImageView) cacheImages.addObject(imageView) } } for imageView in tempImagesArrary{ actionImages.removeObject(imageView) } //TOO-DO 这里要将不显示的imageView从ActionImages移除 for (var i = firstIndex;i<=lastIndex;i  ){ var isShowNewImage = true for imageview in actionImages{ let tagIndex = imageview.tag if tagIndex == i{ isShowNewImage = false } } if isShowNewImage{ showImageAtIndex } } } enum ImageType{ case Local,Web,Resource }}
/**
     * 两点优化
     * 1. 判断convertView是否为空
     * 2. 内部类缓存控价
     */
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view;
        Viewholder viewholder;
        Student student = list.get(position);
        if (convertView == null) {
            view = View.inflate(context, R.layout.item, null);
            viewholder = new Viewholder();
            viewholder.tv_name = (TextView) view.findViewById(R.id.tv_name);
            viewholder.iv_sex = (ImageView) view.findViewById(R.id.iv_sex);
            viewholder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
            view.setTag(viewholder);
        } else {
            view = convertView;
            viewholder = (Viewholder) view.getTag();
        }
        String sex = student.getSex();
        if ("male".equals(sex)) {
            viewholder.iv_sex .setImageResource(R.drawable.nan);
        } else {
            viewholder.iv_sex .setImageResource(R.drawable.nv);
        }
        viewholder.tv_name.setText(student.getName());
        viewholder.iv_delete.setOnClickListener(
                new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Student student = list.get(position);
                        String name = student.getName();
                        dao.delete(name);
                        Toast.makeText(context, "数据被删除了", 0).show();
                        refreshData1();
                    }
                });
        return view;
    }
    class Viewholder{
        TextView tv_name;
        ImageView iv_sex,iv_delete;
    }

tableview的优化一直是一个很考验基本功的活儿,之前做项目的适合被这个问题困扰了很久,通过性能工具、查阅文档解决,整理思路和解决方案如下:
tableview优化最主要:复用cell,header,footer实例;使用约束布局cell子控件时不多次添加约束;图片不过大,尽量不使用透明视图;避免阻塞主线程;计算高度方法不做大量逻辑处理。
cell是否使用了复用机制而不是每一次都创建新的cell。
如果每次都创建新的cell,在滑动的时候会表现为:刚开始的时候很顺畅,但是会越来越卡,内存跟着一直升高,停止滑动的时候也不会降下来。使用缓存机制创建的cell,开始滑动的时候内存会开始上升,等创建了一个屏幕再加半屏的cell之后,内存趋于平稳。
cell是否添加了大量的子控件,或者对layer做了过多的操作。
如果添加了大量的子控件,使用drawRect方法添加子控件,平衡GPU与CPU的负担。同时还需要注意尽量使用不透明视图和不重叠的渐变,否则会加大GPU的负担,造成性能不佳。

思路如下

  public static View inflate(Context context, int resource, ViewGroup root) {
         LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
  }

之前查资料的时候还有用空间换取时间的方案:1)在请求网络数据成功后就计算好高度并通过字典或者数组保存高度值,在高度方法中直接根据数组下标或者key值取得高度并返回。2)还有建立一个frameModel的方法,与1中相似,只是获得网络数据后保存到frameModel中,在frameModel中定义一个类方法,通过获得的model值计算高度后返回。
避免快速滑动情况下开过多线程。
cell中的图片开线程异步加载,相信每个人都会想到。线程开过多了会造成资源浪费,内存开销过大。图片过多时可以不要一滚动就走cellForRow方法,可以在scrollview的代理方法中做限制,当滚动开始减速的时候才加载显示在当前屏幕上的cell(通过tableview的dragging和declearating两个状态也能判断)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var canLoad:Bool = !tableView.dragging && !tableView.declearating if canLoad { //开始loaddata,异步加载图片 }}

下面是代码实现

            以上两种适配器通常用于显示较为简单的条目内容,比如纯文本的显示、简单的文本和图片组合。复杂的条目结构需要用借助BaseaAadpter进行显示。

补充:
简单的设置cornerRadius是不会影响性能的,但是设置了maskToBounds,会导致离屏渲染,应减少设置图层 maskToBounds = YES ,;
使用懒加载图片的方式避免重复下载图片,浪费资源。图片下载后并做压缩处理后将其保存到缓存中,下次加载此图片之前先从缓存中取,如果取不到该图片就在后台下载保存。
使用Core Graphics实现圆角等功能。
重写drawRect方法会离屏渲染,导致内存急剧上升,即使在这个方法里面不写一句代码,也会让内存升高。

在项目中我们经常要实现图片预览这个功能,简单的做法就是往scrollview中添加子控件以实现目的,但是一旦要预览的图片很多的时候,这样的做法会创建很多多余的控件,性能上会大打折扣,下面为大家提供一个思路,来解决这种不开心的事情.

        创建数据库:

图片处理
1)后台下载图片后再回主线程刷新UI,避免阻塞主线程。2)图片过大回造成GPU负担过大,可以在图片下载后压缩尺寸后显示3)避免对layer做过多的操作,尽量设置图片为不透明

** 前提我们只需要创建两个图片视图,因为显示的图片时候我们只会同时显示一张图片,在图片滑动的时候我们才会同时两张图片,照着这样的思路走下去,首先我们得创建两个数组,第一个数组用于存放已创建为使用的图形控件对象,第二个用于存放活动中的图形对象.当滑动控件进行滑动的时候,首先检查缓存数组中是否对象存在,有则取无则创建,并且都需要存放在活动中的数组中,当划过一页边界的时候应当从活动数组中移除消失在视野中图形控件并存放在缓存数组中,以此类推便可.**

        适这里适配器用的是BaseAdapter,单独定义一个类出来继承BaseAdapter,配器程序编写如下:

取得cell的size,进而得到cell高度。通过这个方法获取的cell高度是十分精确的,只要创建好子控件的约束就能获得cell的size。比较不好的是只是这种方法会重走一遍cell的loaddata方法。除此之外在调用cell的loaddata之前需要得到cell的实例,实例创建的方式应该与cellForRow方法一样,优先从缓存池中取得。这个方案可能会创建多个cell。如果能在内存汇总保存一份cell的实例就能解决这个问题了!我讲讲我实现的思路:首先先注册cell,当缓存池中没有cell时系统会自动创建,有的话会直接取缓存中的cell返回给你。
override func viewDidLodad() { tableView.registerClass(CardCell.self, forCellReuseIdentifier: ID)}

         操作数据库,数据库的增删改查:

图片 1

       2.  参考inflate源码,可以获取LayoutInflater 对象,然后调用inflate 方法

补充:请谨慎使用drawRect:
方法,当cell中只有少量的子视图时,应当避免使用,因为重写drawRect:
就是在此方法中一句代码都不写也会占用5-6M的内存。
高度计算方法时不做复杂的计算,尽量只使用加减乘除。
自适应高度的cell实现方式有很多种,比如,1.使用iOS7以上系统的
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
这个方法中,可以先给一个估计的高度,系统会从你给定的高度再去计算实际高度。但是在使用过程中会出现cell突然变高变得低的情况,不适用于高度变化太大的cell。2.如果使用约束布局创建的cell子控件,子控件之间都建立了相互约束,最上面的子控件与cell顶部建立约束,最下面的子控件与cell底部建立了约束,相当于子控件把cell撑开了。

          接下来讲一下ListView的界面优化。       

这时在高度计算方法中,走一遍cell的loaddata方法后可以通过
func systemLayoutSizeFittingSize(targetSize: CGSize) -> CGSize

package com.example.studentsysten;

import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;

import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.example.studentsysten.db.dao.StudentDao;
import com.example.studentsysten.db.domain.Student;

public class MainActivity extends Activity {
    private EditText et_name;
    private RadioGroup rg_sex;
    private ListView lv;
    private StudentDao dao;
    private Myadapter adapter;
    private List<Student> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_name = (EditText) findViewById(R.id.et_name);
        rg_sex = (RadioGroup) findViewById(R.id.rg_sex);
        lv = (ListView) findViewById(R.id.lv);
        dao = new StudentDao(this);
        refreshData();    //初次加载时的刷新数据,每次修改数据时刷新数据
    }
    /**
     * 
     * 添加数据
     * @param view
     */
    public void save(View view){
        String name = et_name.getText().toString().trim();
        if(TextUtils.isEmpty(name)){
            Toast.makeText(MainActivity.this, "姓名不能为空", 0).show();
            return;
        }
        int id = rg_sex.getCheckedRadioButtonId();
        String sex = "male";
        if(id==R.id.male){
            sex = "male";
        }else{
            sex = "female";
        }
        long result = dao.add(name, sex);
        Toast.makeText(this, "数据添加到第" result "行成功", 0).show();
        refreshData();
    }

    /**
     * 
     * 刷新数据,创建条目点击事件
     */
    private void refreshData() {
        list = dao.findAll();
        if(adapter == null ){
            adapter = new Myadapter(MainActivity.this,list);
            lv.setAdapter(adapter);
            lv.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    AlertDialog.Builder builder = new Builder(MainActivity.this);
                    builder.setTitle("详细信息:");
                    builder.setMessage("姓名:"   list.get(position).getName()  "    性别:" list.get(position).getSex() );
                    builder.show();
                }
            });
        }else{
            adapter.refreshData1();
        }
    }
}

用lazy创建一个cell实例,由于lazy 关键字,cell的创建只会执行一次lazy var cell:CardCell = { //已经注册过cell,当缓存池中没有cell时系统会自动创建,有的话会直接取缓存中的cell返回
let v = self.myTableView?.dequeueReusableCellWithIdentifier(self.ID) as! CardCell return v }()

 

约束简图

 

通过懒加载的方式,只创建一次cell的实例,避免内存浪费。接下来要做的步骤就是之前讲的,调用cell的loadData方法,计算高度
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { self.imageCell.loadData(d) let height:CGFloat = self.cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).heightreturn height}

            

        

package com.example.studentsysten.db.domain;

public class Student {
    private String name;
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student [name="   name   ", sex="   sex   "]";
    }

}

        优化二  缓存控件的实例
        获取布局文件中的控件,每次都要在getView()方法中调用 View的findViewById()方法来获取一次控件的实例,当需要多次关心控件时,就会创建多次,因此可以定义一个内部类,用于存放控件,最后将该类的对象存放在view的对象中。当 convertView 为空时,对控件的实例进行缓存,当 convertView 不为空的时候,从内部类中取出控件的实例,这样就减少了调用findViewById()方法的次数了,同样提高了代码的运行效率。

         学生工具类,存放学生信息:

    public ArrayAdapter(Context context, int resource, T[] objects) {
        init(context, resource, 0, Arrays.asList(objects));
    }
package com.example.studentsysten;

import java.util.List;

import com.example.studentsysten.db.dao.StudentDao;
import com.example.studentsysten.db.domain.Student;

import android.content.Context;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Myadapter extends BaseAdapter {
    private List<Student> list;
    private Context context;
    private StudentDao dao;
    private Myadapter adapter;

    public Myadapter(Context context, List<Student> list) {
        this.list = list;
        this.context = context;
        dao = new StudentDao(context);
    }
    /**
     * 
     * 在getview中刷新数据
     */
    public void refreshData1() {
       list = dao.findAll();
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    /**
     * 两点优化
     * 1. 判断convertView是否为空
     * 2. 内部类缓存控价
     */
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view;
        Viewholder viewholder;
        Student student = list.get(position);
        if (convertView == null) {
            view = View.inflate(context, R.layout.item, null);
            viewholder = new Viewholder();
            viewholder.tv_name = (TextView) view.findViewById(R.id.tv_name);
            viewholder.iv_sex = (ImageView) view.findViewById(R.id.iv_sex);
            viewholder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
            view.setTag(viewholder);
        } else {
            view = convertView;
            viewholder = (Viewholder) view.getTag();
        }
        String sex = student.getSex();
        if ("male".equals(sex)) {
            viewholder.iv_sex .setImageResource(R.drawable.nan);
        } else {
            viewholder.iv_sex .setImageResource(R.drawable.nv);
        }
        viewholder.tv_name.setText(student.getName());
        viewholder.iv_delete.setOnClickListener(
                new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Student student = list.get(position);
                        String name = student.getName();
                        dao.delete(name);
                        Toast.makeText(context, "数据被删除了", 0).show();
                        refreshData1();
                    }
                });
        return view;
    }
    class Viewholder{
        TextView tv_name;
        ImageView iv_sex,iv_delete;
    }
}

               图片 2

 

          程序的主逻辑:

  view=getSystemService(Context.LAYOUT_INFLATER_SERVICE).inflate(R.resource, null);
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,int resource, String[] from, int[] to) {
        mData = data;
        mResource = mDropDownResource = resource;
        mFrom = from;
        mTo = to;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
package com.example.studentsysten.db.dao;

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

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.example.studentsysten.db.StudentDBOpenhelper;
import com.example.studentsysten.db.domain.Student;

/**
 * 每一种数据操作都用了两种方法
 * 
 */
public class StudentDao {
    private StudentDBOpenhelper helper;

    public StudentDao(Context context) {
        helper = new StudentDBOpenhelper(context);
    }

    public long add(String name, String sex) {
        SQLiteDatabase db = helper.getWritableDatabase();
        // db.execSQL("insert into student (name,sex) values(?,?)",new
        // Object[]{name,sex});
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("sex", sex);
        long result = db.insert("student", null, values);
        db.close();
        return result;
    }

    public int delete(String name) {
        SQLiteDatabase db = helper.getWritableDatabase();
        // db.execSQL("delete from student where name = ?",new Object[]{name});
        int result = db.delete("student", "name = ?", new String[] { name });
        db.close();
        return result;
    }

    public int update(String name, String sex) {
        SQLiteDatabase db = helper.getWritableDatabase();
        // db.execSQL("update student set sex = ? where name = ?",new
        // Object[]{sex,name});
        ContentValues values = new ContentValues();
        values.put("sex", sex);
        int result = db.update("student", values, "name = ?",
                new String[] { name });
        db.close();
        return result;
    }

    // 根据姓名查性别
    // public String find(String name){
    // SQLiteDatabase db = helper.getReadableDatabase();
    // Cursor cursor = db.rawQuery("select sex from student where name = ?", new
    // String[]{name});
    // // Cursor cursor = db.query("student", new String[]{"sex"}, "name = ?",
    // new String[]{name}, null, null, null);
    // String sex = null;
    // if(cursor.moveToNext()){
    // sex = cursor.getString(0);
    // }
    // return sex;
    // }
    public String find(String name) {
        String sex = null;
        SQLiteDatabase db = helper.getReadableDatabase();
        // Cursor cursor = db.rawQuery("select sex from student where name=?",
        // new String[]{name});
        Cursor cursor = db.query("student", new String[] { "sex" }, "name=?",
                new String[] { name }, null, null, null);
        boolean result = cursor.moveToNext();
        if (result) {
            sex = cursor.getString(0);
        }
        cursor.close();// 释放资源
        db.close();
        return sex;
    }

    public List<Student> findAll() {
        List<Student> list = new ArrayList<Student>();
        SQLiteDatabase db = helper.getReadableDatabase();
        // Cursor cursor = db.rawQuery("select name,sex from student", null);
        Cursor cursor = db.query("student", new String[] { "name", "sex" },
                null, null, null, null, null);

        while (cursor.moveToNext()) {
            String name = cursor.getString(0);
            String sex = cursor.getString(1);
            Student stu = new Student();
            stu.setName(name);
            stu.setSex(sex);
            list.add(stu);
        }
        cursor.close();
        db.close();
        return list;
    }

}

       接下来要说明的是条目点击。 Listview 主要有两种交互方式,一个是条目滚动,另一是条目点击。前者用于显示,后者用于界面交互,点击之后可以进入其他界面或相应的对话框。点击条目归根到底是点击事件,使用 setOnItemClickListener()方法来为 ListView 注册了一个监听器,当用户点击了 ListView 中的任何一个子项时就会回调 onItemClick()方法, 在这个方法中可以通过 position 参数判断出用户点击的是哪一个子项,然后执行接下来的逻辑。条目点击大大地提升了界面的交互性,可以通过点击条目来执行更为复杂的任务。

package com.example.studentsysten.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class StudentDBOpenhelper extends SQLiteOpenHelper {

    public StudentDBOpenhelper(Context context) {
        super(context, "stu.db", null, 1);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table student (_id integer primary key autoincrement,name varchar(30),sex varchar(10))");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub

    }

}

      Listview应该是最为常见的控件。对于大多数规则排列的界面,几乎都可以用ListView进行编写。对于单一界面来说,ListView既是最难的控件,又是使用最为频繁的控件。ListView 通常用于展示大量的数据,比如读取数据库中的数据。ListView优势也较为明显,比如显示大量数据时节约内存,自带ScrollView 的功能可以实现界面滚动等。ListView 控件的设计正好遵循MVC 设计模式。mode 数据数据模型,在ListView中具体可以指要显示到ListView 上的数据集合;view 视图用于展示数据,数据显示在每一个条目上;controller 控制层,数组中的数据是无法直接传递给 ListView 的,需要借助适配器把数据展示到控件上。因此有必要对ListView的使用进行一下总结。

          工程目录结构:

         接着是对代码的优化。通常使用ListView时涉及到的数据不会太少,测试的时候几十上百条的数据可能看不出影响,但当数据达到成千上万条的时候,未优化的listView很有可能出现异常。因此代码优化是十分有必要的。这里介绍两处代码的优化处理,一个是convertView的复用,另一个是控件缓存机制。

 
      2. SimpleAdapter
          使用SimpleAdapter 的数据一般都是HashMap构成的List集合,List 的每一个对象对应ListView 的每一行。HashMap 的每个键值数据映射到布局文件中对应id 的组件上。这样可以方便的显示图文显示的条目,通常设置界面就是这种形式。

          

        listview复用具体的做法是在 getView()方法中首先进行了判断convertView 的内容是否为空,如果convertView的内容为空则用layoutInflater 去加载布局,如果不为空则直接对 convertView 进行复用。这样就大大提高了ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能,再也不用担心内存溢出了。

       三种方式差别并不大,可任意选择。得到View对象后,可以通过View对象找到布局文件中的控件,布局文件可以单独定义一个xml文件用来表示每一个Item条目的显示效果。

       

  LayoutInflater.from(context).inflate(resource, null)     

      Listview使用步骤并不复杂,无论是哪种场景下使用都可以归纳为以下几步:
      1.  布局文件里申明ListView ,设置id号和其他相关参数
      2.  在代码中通过findViewById方法找到listView控件
      3.  设置适配器,可以单独定义出一个类继承相关的适配器,也可以创建一个适配器的匿名内部类。关键取决于代码的复杂程度。
      4.  重写适配器中的方法。主要有两个,一个是getCount()表示listview的条目数。另一个是getView返回显示的View ,表示每一个条目的视图。getView()方法在每个子项被滚动到屏幕内的时候会被调用。
      5.  ListView列表项的点击事件 listView.setOnItemClickLinstener()

  /**
     * 
     * 刷新数据,创建条目点击事件
     */
    private void refreshData() {
        list = dao.findAll();
        if(adapter == null ){
            adapter = new Myadapter(MainActivity.this,list);
            lv.setAdapter(adapter);
            lv.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    AlertDialog.Builder builder = new Builder(MainActivity.this);
                    builder.setTitle("详细信息:");
                    builder.setMessage("姓名:"   list.get(position).getName()  "    性别:" list.get(position).getSex() );
                    builder.show();
                }
            });
        }else{
            adapter.refreshData1();
        }
    }

        优化后的代码:

          至此,ListView的全部内容讲解完毕。接下来用一个实例来巩固以上的知识点。

   

     

        另外还用到数据库的知识,需要创建三个类出来,分别是创建数据库、操作数据库、学生工具类。

       3.通过上下文提供的getSystemService 方法获取LayoutInfater 对象,然后调用inflate 方法

            context表示上下文。data表示要显示的数据,用键值对的形式暂时存放数据。resource是布局文件的ID号。from表示Map 集合中key 的数组,to表示item 布局中控件的id。后两个参数均是字符转数组,表示的是多个Item条目。

           第一个参数context表示上下文。 resource是布局文件的ID号,objects表示要显示的数组或List集合。

          设计一个学生信息管理系统用于记录学生姓名和性别,用到的知识点包括数据库、listview显示。首先搭建软件界面,需要一个输入框和单选框用于编辑学生的姓名和性别。还需要一个按钮用于保存数据,提交数据。然后需要一个listview用于显示所有学生的信息。对于每一个条目来说,可以用图片来区别男女,用文本显示学生姓名。最后还可以添加一个删除的图片对数据进行删除操作。接下来是逻辑部分。首先通过设置点击事件,当按钮按下时,将文本框和单选框里面内容写入数据库,同时通知Listview刷新界面,这样就可以看到listview中的数据添加了一行。同样的将删除图标设置为一个点击事件,点击删除图标时删除数据库中的相关信息,同时通知Listview刷新界面,这样界面上的listview就会减少一行。最后可以为每一个条目添加一个条目点击事件。当条目被点击时,弹出一个对话框,列出学生的详细信息。至此,用到listview的大部分知识点。

         优化一   listview的复用
         convertView用于将之前加载好的布局进行缓存,以便之后可以进行复用。通过使用convertView 对创建的视图对象进行复用,可以节约减少内存消耗。对于少量相同形式的数据可以用LinearLayout代替显示,但是当数据增加到上千条、上万条的时候,快速滚动滑动条就会不断地生成新的TextView,对于内存的消耗过大,容易造成内存溢出。Listview自带上下滑动的功能,因此可以将滑出屏幕的convertView进行回收利用,每当一个item看不见的时候,那个item就可以被复用起来了,这样,ListView 始终保持创建的对象个数为屏幕显示的条目的个数加一。事实上,item的view对象没有真正的被垃圾回收器回收掉,而是重新将身上的数据给换掉了,看起来好像是出现了一个新的item。这样视觉上就是连续的滚动条了。

          对于ArrayAdapter来说可以使用Android 系统提供的简单布局,比如android.R.layout.simple_list_item_1。但这种方式较为固定,无法满足实际开发多样化的需要。因此可以重新创建一个布局文件,对界面进行自定义,根据自己的需要添加相应的控件。这样可以丰富界面的效果,提升软件美感,适应不同的开发需求。View对象本身或者任何继承View对象的控件都有一个方法,那就是inflate。通过inflate方法可以将一个布局文件转换成一个View对象,布局里面的所有控件都可以通过这个View对象来进行查找。Inflate的使用方式有以下几种。
       1. 直接用View.inflate()创建View对象,这是View自带的方法。

      1.   ArrayAdapter的使用
            ArrayAdapter通常用于显示较为简单的数组和集合数据,界面较为简单,直接向ArrayAdapter添加相关的参数即可。

      知道Listview的使用步骤后,还有很多需要注意的细节问题。接下来一一列举出来。

          

           最后一个小细节不要忘了,那就是数据更新。很多人在编写listview过程中经常会忘了数据更新,这样导致的结果就是,对数据进行增删改操作时界面不能及时刷新,只有重启程序,才可以看到界面上的数据发生改变。Listview的界面刷新有两种方式。一种是在getview中直接进行刷新,另一种则是单独定义一个方法对数据进行刷新。这里介绍第二种。在单独定义的数据方法中,通常要做的是,首先得到要在listview中显示的数据,可以将数据存放在List集合中。然后判断适配器对象是否为空,若为空则定义一个新的适配器出来,否则直接调用适配器的notifyDataSetChanged()方法进行刷新。数据刷新是Listview中的一个小细节,稍微一步注意就会影响到界面的显示效果,因此要引起格外的注意。

      首先是适配器的类型选择。Android 中提供了很多适配器的实现类,通常情况下使用的都是BaseAdapter,而其他的适配器都是继承自BaseAdapter。但有些界面控件很少,利用ArrayAdapter或者SimpleAdapter就可以满足需求。所以在这里还是稍微介绍一下ArrayAdapter和SimpleAdapter的使用。文章最后给出一个学生信息管理系统的案例,用的适配器是BaseAdapter,所以这里就不再对BaseAdapter的使用再做讲解。

 

本文由新葡京8455发布,转载请注明来源

关键词: