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

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

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

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

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

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

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

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

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);
}

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

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

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

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

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

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

优化后的代码:

/**
* 两点优化
* 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;
}

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

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

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

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

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

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

  view=getSystemService(Context.LAYOUT_INFLATER_SERVICE).inflate(R.resource, null);

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

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

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

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

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

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

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;
}
}

程序的主逻辑:

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();
}
}
}

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

创建数据库:

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 } }

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

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;
} }

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

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 + "]";
} }

工程目录结构:

Listview详解的更多相关文章

  1. [置顶] 安卓UI组件之ListView详解

    ListView是很常见的一个UI组件,在许多App中都很常用,其意思就是可滚动的列表,使用ListView必须使用Adapter(适配器),常用的适配器友谊ArrayAdapter,SimpleAd ...

  2. Android软件开发之ListView 详解【转】

    ListView的使用方法  ListView是Android软件开发中非常重要组件之一,基本上是个软件基本都会使用ListView ,今天我通过一个demo来教大家怎么样使用ListView组件 绘 ...

  3. 【Android UI】Android ListView详解

    在android开发中ListView是比较常用的组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示.抽空把对ListView的使用做了整理,并写了个小例子,如下图. 列表的显示需要三 ...

  4. Android ListView 详解

    我做Android已经有一段时间了,想想之前在学习Android基础知识的时候看到了许许多多博主的博文 和许多的论坛.网站.那时候就非常感谢那些博主们能吧自己的知识分享在互联网上,那时候我就想 如果我 ...

  5. Android列表控件ListView详解

    ListView绝对可以称得上是Android中最常用的控件之一,几乎所有应用程序都会用到它. 由于手机屏幕空间都比较有限,能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候 ...

  6. 淘宝(阿里百川)手机客户端开发日记第四篇 自定义ListView详解

    我们知道,如果采用官方的ListView,实现的功能在很多时候,并不能满足自己的业务需求,比如在设计到复杂的列表的时候,这一节,我们就开始动手自己实现自定义的ListView. 在上一节中,我们采用了 ...

  7. Android的ListView详解

    在android开发中ListView是比较常用的组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示.抽空把对ListView的使用做了整理,并写了个小例子,如下图. 列表的显示需要三 ...

  8. android ListView详解继承ListActivity

    [转]http://www.cnblogs.com/allin/archive/2010/05/11/1732200.html 在android开发中ListView是比较常用的组件,它以列表的形式展 ...

  9. Android中 ListView 详解(二)

    本文版权归 csdn noTice501 所有,转载请详细标明原作者及出处,以示尊重! 作者:noTice501 原文:http://blog.csdn.net/notice520/article/d ...

随机推荐

  1. Android之genymotion模拟器安装于配置

    今天是双休日,可怜没人陪,只好一个人玩电脑了,之前别人一直给我推荐genymotion模拟器,说各种方便,秒杀Android自带模拟器,所以就趁这个周末搞了一下,总体感觉还挺不错的,确实比Androi ...

  2. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  3. Android okHttp网络请求之Get/Post请求

    前言: 之前项目中一直使用的Xutils开源框架,从xutils 2.1.5版本使用到最近的xutils 3.0,使用起来也是蛮方便的,只不过最近想着完善一下app中使用的开源框架,由于Xutils里 ...

  4. Oracle Database 11g Express Editon介绍及安装

    一.Oracle Database 11g Express版本介绍 公司项目开发中,使用的数据库是Oracle 10g和MySQL 5.5,最新因为开发需要,需要从后台读取一些数据.使用的客户端是PL ...

  5. Java面向对象练习

    1.定义长方形类,含: 属性:宽.高(整型): 方法:求周长.面积: 构造方法3个:(1)无参——宽.高默认值为1:(2)1个参数——宽.高均为参数值:(3)2个参数——宽.高各为参数值. 要求:进行 ...

  6. Servlet在启动时加载的tomcat源码(原创)

    tomcat 8.0.36 知识点: 通过配置loadOnStartup可以设置Servlet是否在Tomcat启动时加载,以及按值大小进行有序加载,其最小有效值为0,最大有效值为Integer.MA ...

  7. PHP中的魔术方法(2)

    1.__get.__set这两个方法是为在类和他们的父类中没有声明的属性而设计的__get( $property ) 当调用一个未定义的属性时访问此方法__set( $property, $value ...

  8. 初来乍到 Java 和 .Net 迭代器功能

    最近有一个需求是这样的, 根据键值对存储类型数据,也算是数据缓存块模块功能设计. 一个键对应多个值.每一个键的值类型相同,但是每个不同的键之间类型不一定相同. Java 设计如下 HashMap< ...

  9. 通过Canvas + JS 实现简易时钟实战

    最近通过各种渠道学习了下html5中的canvas元素,为了练练手就随手写了一个简易的时钟.时钟本身不复杂,没有使用图片进行美化,下面就与大家分享一下具体的代码: 这是最终实现的效果: 部分的启发点来 ...

  10. 兼容性背景颜色半透明CSS代码(不影响内部子元素)

    如何简单兼容性的实现父元素是半透明背景色,而子元素不受影响. 兼容所有浏览器的背景颜色半透明CSS代码: background-color: rgba(, , , .); filter: progid ...