首先是SharedPreferences 用户偏好

package com.example.wkp.aboutdata;

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText; /**
* SharedPreferences:用户偏好
*用来保存一些简单的配置信息
*
* 存储过程:
* 1.针对SharedPreferences初始化
* 2.获取Editor对象
* 3.调用commit方法提交数据
*
*
* 取出数据:
* 1.获取SharedPreferences对象
* 2.需要在哪里使用SharedPreferences保存文件中的值取出进行使用
*
* 注意:SharedPreferences保存数据的路径:data/data/应用程序包名/shared_prefs
*/
public class MainActivity extends AppCompatActivity { private Button btn;
private EditText userName,password;
private SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //参数一:表示存储的文件名,参数二:存储文件的操作格式。经常使用MODE_PRIVATE即本应用中可以读取
//扩展名默认是xml格式
sharedPreferences=getSharedPreferences("login",MODE_PRIVATE); //初始化控件
btn=(Button)findViewById(R.id.login);
userName=(EditText)findViewById(R.id.userName);
password=(EditText)findViewById(R.id.password); //取出数据并且渲染
userName.setText(sharedPreferences.getString("user",""));
password.setText(sharedPreferences.getString("pass","")); btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { String user=userName.getText().toString();
String pass=password.getText().toString(); //获取editor对象
SharedPreferences.Editor editor=sharedPreferences.edit();
editor.putString("user",user);
editor.putString("pass",pass);
//调用commit方法进行提交
editor.commit(); Intent i=new Intent(MainActivity.this,SecondActivity.class);
i.putExtra("name",user);
i.putExtra("pass",pass);
startActivity(i);
}
});
}
}

MainActivity.java

然后是外部存储,将信息保存在SD卡中

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; /**
* Created by wkp on 2016/9/25.
* 外部存储:
* 保存外部的SD卡中,使用之前确定SD卡可用
* 确保当前SD卡可用
* 写入文件需要权限
*
*/
public class OutStoreActivity extends Activity implements View.OnClickListener {
private Button out_save, out_show;
private ImageView out_image;
private boolean flag = true;
private File file;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_outstore); //初始化控件
out_save = (Button) findViewById(R.id.out_save);
out_show = (Button) findViewById(R.id.out_show);
out_image = (ImageView) findViewById(R.id.out_image); //点击事件
out_save.setOnClickListener(this);
out_show.setOnClickListener(this); //获取当前外部存储设备的状态
String state = Environment.getExternalStorageState();
//判断当前的SD卡是否可用
if (state.equals(Environment.MEDIA_MOUNTED)) {
Log.v("TAG", "当前SD卡可用");
} else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
Log.v("TAG", "当前SD卡只读权限");
flag = false;
} else {
Log.v("TAG", "当前SD卡不可用");
flag = false;
}
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.out_save://点击保存
if (flag) {
saveImage();
}
break;
case R.id.out_show://点击显示
if (flag) {
showImage();
}
break;
}
} //保存图片
public void saveImage() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//获取当前SD的根路径
File sdPath = Environment.getExternalStorageDirectory();
File file = new File(sdPath,"studio.jpg");
//读取项目中的图片
InputStream is = getResources().openRawResource(R.drawable.some);
OutputStream os = new FileOutputStream(file);
//缓存加快读写效率
bis = new BufferedInputStream(is);
bos = new BufferedOutputStream(os);
int len = 0;
byte[] buf = new byte[1024];
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
bos.flush();
}
Toast.makeText(this,"写入完毕",Toast.LENGTH_SHORT).show();
ScanFile(file.getPath());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
//扫描的方法
private void ScanFile(String path){
MediaScannerConnection.scanFile(this,new String[]{path},null,null);
} //显示图片
public void showImage() {
//从文件夹读取图片
Bitmap bitmap=BitmapFactory.decodeFile(file.getPath());
out_image.setImageBitmap(bitmap);
}
}

OutStoreActivity.java

内部存储

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; /**
* Created by wkp on 2016/9/26.
* 内部存储:
* 打开内部存储文件使用的方法:
* openFileOutput来打开一个输出流关联内部存储保存的文件保存
* openFileInput来打开一个输入流关联内部存储保存文件进行关联
*
* 常用的缓存路径:
* 内部缓存:context.getCacheDir()
* 外部存储的缓存路径:context.getExternalCacheDir()
*/
public class InnerStoreActivity extends Activity implements View.OnClickListener { private Button inner_save, inner_show;
private EditText inner_tv; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_innerstore); //初始化控件
inner_tv = (EditText) findViewById(R.id.inner_tv);
inner_save = (Button) findViewById(R.id.inner_save);
inner_show = (Button) findViewById(R.id.inner_show); //点击事件
inner_save.setOnClickListener(this);
inner_show.setOnClickListener(this); //应用程序中经常使用的缓存路径
Log.i("TAG","保存的路径1:"+this.getCacheDir());
Log.i("TAG","保存的路径2:"+this.getExternalCacheDir());
Log.i("TAG","保存的路径3:"+ Environment.getDownloadCacheDirectory()); } @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.inner_save://点击保存
saveInner();
break;
case R.id.inner_show://点击显示
showInner();
break;
}
} //显示内部存储文件中的内容
private void showInner() {
FileInputStream fis = null;
try {
fis = openFileInput("inner.txt");
int len = 0;
byte[] buf = new byte[1024];
//只要不是读到文件末尾就一直读取
while ((len = fis.read(buf)) != -1) {
Toast.makeText(this, "获取到了:" + new String(buf, 0, len), Toast.LENGTH_SHORT).show();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} } //保存的方法
private void saveInner() {
FileOutputStream fos = null;
//获取EditText内部输入的内容
String input = inner_tv.getText().toString();
try {
//打开内部存储的文件需要指明文件名和该文件的操作模式
fos = this.openFileOutput("inner.txt", MODE_PRIVATE);
fos.write(input.getBytes());
Toast.makeText(this, "保存到内部存储文件完毕", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

InnerStoreActivity.java

使用sqlite进行数据存储

package com.example.wkp.aboutdata.com.example.wkp.aboutdata.util;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; /**
* Created by wkp on 2016/9/26.
* 操作数据库的公告类
* db.exexSQL:一般用来执行增加,删除,修改的sql语句
*/
public class MyOpenHelper extends SQLiteOpenHelper { public static String CREATE_TABLE_PERSON="create table Person("+
"id integer primary key autoincrement,"+
"name varchar(20),"+
"address varchar(40))"; public static final String ALTER_TABLE_PERSON="alter table person add age int";
public static final String CREATE_TABLE_CATEGORY="create table category("+
"id integer primary key autoincrement,"+
"type int)"; public MyOpenHelper(Context context) {
super(context, "Message.db", null, 3);
} //数据库创建的时候执行的方法
@Override
public void onCreate(SQLiteDatabase db) {
Log.i("TAG","oncreate方法被执行"); //执行sql
db.execSQL(CREATE_TABLE_PERSON);
} //数据库升级执行的方法
//修改表中的字段,增加表
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("TAG","onupgrade方法被执行");
//升级数据库中做到后续新版本的数据库使用的过程中获取之前所有的数据库表以及更新的所有字段,不设break
switch(oldVersion){
case 1:
db.execSQL(ALTER_TABLE_PERSON);
case 2:
db.execSQL(CREATE_TABLE_CATEGORY);
}
} //数据库的降级
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onDowngrade(db, oldVersion, newVersion);
}
}

MyOpenHelper.java

package com.example.wkp.aboutdata.com.example.wkp.aboutdata.util;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import com.example.wkp.aboutdata.com.example.wkp.aboutdata.entity.Person; import java.util.ArrayList;
import java.util.List; /**
* Created by wkp on 2016/9/26.
* 封装了增删查改的方法
* 增删改:使用的都是db.execSql(sql,new Object[]{})
* 操作数据库的时候可以将插入和修改写成一个方法
* 查询出来的字段必须要有_id
*/
public class PersonDao {
private SQLiteDatabase db; public PersonDao(Context context) {
MyOpenHelper myOpenHelper = new MyOpenHelper(context);
//满了后以只读形式打开 后者报错
//获取可以操作的数据库对象
db = myOpenHelper.getReadableDatabase();
} //插入的方法
public void insert(Person person) {
// String sql="insert into person (name,age,address) values ('张三',13,'北京')";
//1.使用拼接sql的形式进行数据的插入
String sql = "insert into person (name,age,address) values ('" + person.getName() + "'," + person.getAge() + ",'" + person.getAddress() + "')";
// db.execSQL(sql);
//2.使用sql占位符的形式
String sql1 = "insert into person (name,age,address) values (?,?,?)";
// db.execSQL(sql1,new Object[]{person.getName(),person.getAge(),person.getAddress()});
//3.使用系统自带的方法
ContentValues contentValues = new ContentValues();
contentValues.put("name", person.getName());
contentValues.put("age", person.getAge());
contentValues.put("address", person.getAddress());
db.insert("person", null, contentValues);
} //修改的方法
public void updata(Person person, int id) {
//1.使用拼接sql的形式进行数据的更新
String sql = "update person set name = '" + person.getName() + "' where id=" + id;
// db.execSQL(sql);
//2.使用sql占位符的形式
String sql1 = "updata person set name = ? where id = ?";
// db.execSQL(sql,new Object[]{person.getName(),id});
//3.使用系统自带的方法
ContentValues contentValues = new ContentValues();
contentValues.put("name", person.getName());
db.update("person", contentValues, "id = ?", new String[]{id + ""});
} //删除的方法
public void delete(int id) {
//1.使用sql语句拼接
String sql = "delete from person where id = " + id;
// db.execSQL(sql);
//2.占位符
String sql1 = "delete from person where id = ?";
// db.execSQL(sql,new Object[]{id});
//3.系统自带的方法
db.delete("person", "id=?", new String[]{id + ""});
} //查询的方法
public Cursor query() {
String sql = "select id as _id,name,age,address from person";
Cursor cursor = db.rawQuery(sql, null);//查询出来的数据都是封装在cursor游标中
return cursor;
} /**
* 分页查询执行的方法
*
* @param page 需要查询的页码
* @param capcity 每页中显示的容量
* @return List<Person>
*/
//分页查询
public List<Person> queryByPage(int page, int capcity) {
List<Person> list = new ArrayList<Person>();
//占位符 表示从哪条数据开始查询,表示向后查询多少数据
String sql = "select * from person limit ?,?";
Cursor cursor = db.rawQuery(sql, new String[]{(page - 1) * capcity + "", capcity + ""});
if (cursor != null) {
//遍历cursor取出游标中的内容
while (cursor.moveToNext()) {
//取出数据
String name = cursor.getString(cursor.getColumnIndex("name"));
String address = cursor.getString(cursor.getColumnIndex("address"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
Person p = new Person(id, name, age, address);
list.add(p);
}
//关闭cursor
cursor.close();
}
return list;
} }

PersonDao.java

package com.example.wkp.aboutdata.com.example.wkp.aboutdata.entity;

/**
* Created by wkp on 2016/9/26.
*/
public class Person {
private int id;
private String name;
private int age;
private String address; public Person() {
} public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
} public Person(int id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address;
}
}

Person.java

简单的增删查改

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.SimpleCursorAdapter; import com.example.wkp.aboutdata.com.example.wkp.aboutdata.entity.Person;
import com.example.wkp.aboutdata.com.example.wkp.aboutdata.util.PersonDao; /**
* Created by wkp on 2016/9/26.
* 操作数据库的页面
*/
public class SqliteActivity extends Activity implements View.OnClickListener { private PersonDao personDao;
private Button insert;
private Button delete;
private Button update;
private Button query; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite); //初始化
insert = (Button) findViewById(R.id.sqlite_insert);
delete = (Button) findViewById(R.id.sqlite_delete);
update = (Button) findViewById(R.id.sqlite_update);
query = (Button) findViewById(R.id.sqlite_query);
insert.setOnClickListener(this);
delete.setOnClickListener(this);
update.setOnClickListener(this);
query.setOnClickListener(this);
personDao = new PersonDao(this);
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sqlite_insert://插入
for (int i = 0; i < 40; i++) {
Person p = new Person("天津", i, "和和" + i);
personDao.insert(p);
}
break;
case R.id.sqlite_delete://删除
personDao.delete(4);
break;
case R.id.sqlite_update://更新
Person p2 = new Person("天哪噜", 10, "和和");
personDao.updata(p2, 3);
break;
case R.id.sqlite_query://查询
startActivity(new Intent(this, QueryByPageActivity.class));
break;
}
}
}

SqliteActivity.java

使用事务

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button; import com.example.wkp.aboutdata.com.example.wkp.aboutdata.util.MyOpenHelper; /**
* Created by wkp on 2016/9/27.
* sqlite数据库中使用事务的方法
* 剔除person中的所有的数据,插入一条新的数据
* 保证删除和插入一起完成
* 1.开启事务 db.beginTransaction
* 2.设置事务的成功提交
* 3.关闭事务对象
*/
public class SqliteTransactionActivity extends Activity { private Button transaction_btn;
private MyOpenHelper myOpenHelper; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transaction);
transaction_btn = (Button) findViewById(R.id.transac);
myOpenHelper = new MyOpenHelper(this);
transaction_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = myOpenHelper.getReadableDatabase();
//开启事务
db.beginTransaction();
try {
db.delete("person", null, null);
// if (true) {
// //程序执行到这里直接抛出异常
// throw new NullPointerException();
// }
ContentValues values = new ContentValues();
values.put("name", "赫赫");
values.put("age", 88);
values.put("address", "福建");
db.insert("person", null, values);
db.setTransactionSuccessful();//事务已经执行成功
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
} }
});
}
}

SqliteTransactionActivity.java

数据呈现

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter; import com.example.wkp.aboutdata.com.example.wkp.aboutdata.util.PersonDao; /**
* Created by wkp on 2016/9/27.
* 数据库查询:
* 数据源:cursor对象
* 如果在页面中显示数据库内容,可以使用SimpleCursorAdapter
* 使用该适配器的时候注意查询出来的id字段必须命名成_id
*/
public class SimpleCursorAdapterActivity extends Activity { private ListView listView;
private SimpleCursorAdapter simpleCursorAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simplecursor);
listView = (ListView) findViewById(R.id.sqlite_listview);
PersonDao personDao = new PersonDao(this);
Cursor cursor = personDao.query();
// Log.v("ppp","记录条数:"+cursor.getCount());
//参数一:上下文对象 参数二:适配器对应的item布局 参数三:数据源cursor 参数四:字符串类型的数组 参数五:int类型数组
String[] from = {"name", "age", "address"};//从数据库中查询出来的需要显示的字段组成的数据
int[] to = {R.id.adapter_name, R.id.adapter_age, R.id.adapter_address};//查询出来的字段需要显示到的控件id组成的数组
simpleCursorAdapter = new SimpleCursorAdapter(this, R.layout.simple_cursor_adapter_item, cursor, from, to, 0);
listView.setAdapter(simpleCursorAdapter);
}
}

SimpleCursorAdapterActivity.java

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="name"
android:gravity="center"
android:textSize="20sp"
android:id="@+id/adapter_name"
android:layout_weight="1"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="age"
android:gravity="center"
android:textSize="20sp"
android:id="@+id/adapter_age"
android:layout_weight="1"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="address"
android:gravity="center"
android:textSize="20sp"
android:id="@+id/adapter_address"
android:layout_weight="1"/>
</LinearLayout>

simple_cursor_adapter_item.xml

分页呈现

package com.example.wkp.aboutdata;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView; import com.example.wkp.aboutdata.com.example.wkp.aboutdata.entity.Person;
import com.example.wkp.aboutdata.com.example.wkp.aboutdata.util.PersonDao; import java.util.ArrayList;
import java.util.List; /**
* Created by wkp on 2016/9/27.
*/
public class QueryByPageActivity extends Activity {
private ListView listView;
private MyAdapter myAdapter;
private List<Person> list;
private PersonDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simplecursor);
listView=(ListView)findViewById(R.id.sqlite_listview);
dao=new PersonDao(this); //分页查询显示的数据源
list=dao.queryByPage(2,10);
myAdapter=new MyAdapter();
//显示
listView.setAdapter(myAdapter);
} //显示的适配器类
class MyAdapter extends BaseAdapter{ @Override
public int getCount() {
return list.size();
} @Override
public Person getItem(int position) {
return list.get(position);
} @Override
public long getItemId(int position) {
return getItem(position).getId();
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder=null;
if(convertView==null){
//映射对应的布局给ConvertView赋值
convertView= LayoutInflater.from(QueryByPageActivity.this).inflate(R.layout.simple_cursor_adapter_item,null);
holder=new Holder();
holder.address= (TextView) convertView.findViewById(R.id.adapter_address);
holder.age= (TextView) convertView.findViewById(R.id.adapter_age);
holder.name= (TextView) convertView.findViewById(R.id.adapter_name);
//打标签
convertView.setTag(holder);
}else{
//取出标签
holder=(Holder)convertView.getTag();
}
//赋值
holder.address.setText(getItem(position).getAddress());
holder.name.setText(getItem(position).getName());
holder.age.setText(getItem(position).getAge()+"");
return convertView;
}
} //复用类
class Holder{
TextView name,age,address;
}
}

QueryByPageActivity.java

最新文章

  1. kzalloc 函数详解(转载)
  2. 用任意语言与WebService进行交互
  3. oracle连接本地数据库
  4. 导出excel乱码问题
  5. how-to-install-hyper-v-on-a-virtual-machine-in-hyper-v.aspx
  6. linux消息队列操作
  7. 用Ghostscript API将PDF格式转换为图像格式(C#)
  8. java 基础的几种算法
  9. 不使用数据结构反转栈 递归 CVTE实习 CVTE是一家什么公司
  10. java上传文件代码
  11. js 数字数组按大小排序
  12. 腾讯QQ积分CSRF导致积分任意挥霍(我的积分为什么少了)
  13. 【咸鱼教程】protobuf在websocket通讯中的使用
  14. pip安装包
  15. AssertionError
  16. .NET面试宝典-高级2
  17. MySQL增强版命令行客户端连接工具(mycli)
  18. 3dsmax sendto mudbox失效解决方案
  19. 判断是否是IE浏览器和是否是IE11
  20. AspnetBoilerplate (ABP) Organization Units 组织结构管理

热门文章

  1. User has no SELECT privilege on V$SESSION
  2. Centos 6.8下安装LBP2900打印机驱动
  3. H264源码分析(二)
  4. 依赖注入及AOP简述(六)——字符串请求模式 .
  5. Windowsclient开发简单介绍(四)
  6. About Quick Packaging and Custom Packaging
  7. DevExpress.GridControl.gridView的一些注意
  8. linux通过文件查找依赖关系
  9. JBOSS javax.naming.NameNotFoundException: xxx not bound
  10. javaweb一周总结(菜鸟)