简单图库软件的实现(联网下载图片保存到sdcard在Listview中展示,并作为ContentProvider为其他软件提供图库数据)
先看效果图:
这就是一个可以联网的图库软件,下面我们来看看需求
业务需求
1.判断是否第一次运行,第一次运行,提示添加新条目
2.点击添加按钮,弹出对话框,输入图片网址和标题
3.下载图片保存到本地SD卡中
4.数据库中保存图片文件路径和图片标题和URL地址
5.listview中列出已保存的所有条目,添加条目后,同步展现到listview中
6.选中listview中一个条目,点击删除,删除存储的条目,同步展现到listview
7.长按listview的条目,弹出删除菜单项,点击菜单项也可以删除条目
8.提供contentprovider供其他软件访问数据库
问题分析
虽然要求看起来挺多但是可以大致分为3部分去实现
主要代码实现
首先用户输入图片地址我们应该去下载图片并保存到本地,此时下载图片属于耗时且需要联网的操作所以不能在ui线程中实现,我们创建异步任务AsyncTask完成图片下载,并通知UI线程更新进度条界面。下载完成应该返回图片的保存地址准备将数据写入数据库中 由于要展示下载的进度我们利用接口回调比较好实现。下面是下载图片的异步任务代码:
import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL;import android.os.AsyncTask; import android.os.Environment;public class MyTask extends AsyncTask<String, Integer, String> {public interface CallBack {public void start(); //主界面展示一个进度条public void updataProgress(int progress); //更新进度条public void finish(String imgPath); //下载完成返回文件保存到绝对路径}CallBack cb;public MyTask(CallBack cb) {super();this.cb = cb;}@Overrideprotected void onPreExecute() {super.onPreExecute();if (cb != null) {cb.start(); //准备工作}}@Overrideprotected String doInBackground(String... params) {// 1.HttpURLConnectionHttpURLConnection conn = null;String imgPath = null;// 2.URLtry {URL url = new URL(params[0]);// 3.url.openConnectionconn = (HttpURLConnection) url.openConnection();// 4.InputStreamInputStream in = conn.getInputStream();// 获取该文件的总长度int total = conn.getContentLength();// 5.获取保存文件的路径及文件 名/sdcard/imageString path_sdcard = Environment.getExternalStorageDirectory().getAbsolutePath() + "/image";File fileParent = new File(path_sdcard);// 判断该目录是否存在,如果不存在,创建该目录if (!fileParent.exists()) {// 创建目录fileParent.mkdirs();}String arr[] = params[0].split("/");String filenameString = arr[arr.length - 1];// 6.创建File对象,再拿到OutputStreamFile file = new File(path_sdcard, filenameString);if (file.exists()) {return file.getAbsolutePath();}// 用来返回该img路径imgPath = file.getAbsolutePath();OutputStream out = new FileOutputStream(file);byte[] buffer = new byte[4096];int sum = 0;int len = 0;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);// 累加sum = sum + len;// 计算百分比int per = (int) (sum * 100f / total);// 发布进度值publishProgress(per);}out.flush();out.close();in.close();// 返回当前被保存的img的绝对路径return imgPath;} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (conn != null) {conn.disconnect();}}//下载异常返回NULLreturn null;}@Overrideprotected void onProgressUpdate(Integer... values) {// TODO Auto-generated method stubsuper.onProgressUpdate(values);if (cb != null) {cb.updataProgress(values[0]); //更新进度条}}/** result表示的是图片所在的的路径*/@Overrideprotected void onPostExecute(String result) {// TODO Auto-generated method stubsuper.onPostExecute(result);if (cb != null) {cb.finish(result); //返回图片地址}}}图片下载完成并且保存到本地了 此时我们应该将图片名称 URL 绝对路径 写入数据库。 那么此时我们应该开始创建数据库了,自定义MySqliteHelper 继承SQLiteOpenHelper 就好了,代码如下
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log;public class MySqliteHelper extends SQLiteOpenHelper {public MySqliteHelper(Context context) {super(context, "picture.db", null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {// 创表String sql = "create table img(_id integer primary key autoincrement,name text ,url text,path text)";db.execSQL(sql);Log.d("onCreateDataBase", "helper onCreate create table img");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub}}数据库有着落了,那么我们应该想着如何将数据展示到listview中,这里ListView中的每个item包含了一张图片一个文本 。 这里就用就灵活的BaseAdapter完成,我们自定义一个MyAdapter继承自BaseAdapter。
import java.io.File; import java.util.ArrayList; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast;public class MyAdapter extends BaseAdapter {ArrayList<Picture> data;Context context;LayoutInflater inflater;int progress;public MyAdapter(ArrayList<Picture> data, Context context) {super();this.data = data;this.context = context;inflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return data.size();}@Overridepublic Object getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubViewHolder holder = null;if (convertView == null) {holder = new ViewHolder();convertView = inflater.inflate(R.layout.list_item, null);holder.tv_1 = (TextView) convertView.findViewById(R.id.tv);holder.iv = (ImageView) convertView.findViewById(R.id.img);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}Picture picture = data.get(position);File file = new File(picture.path);if (file.exists()) { // 如果sd卡的图片存在去设置图片holder.tv_1.setText(picture.name);Bitmap bitmap = BitmapFactory.decodeFile(picture.path);holder.iv.setImageBitmap(bitmap);} else { // 图片不存在提示用户Toast.makeText(context, picture.name + "好像出了点问题,图片是否被你删除?",Toast.LENGTH_SHORT).show();// 本地图片被删除 删除数据库中的数据MySqliteHelper helper = new MySqliteHelper(context);SQLiteDatabase db = helper.getWritableDatabase();String sql = "DELETE FROM img WHERE path = '" + picture.path + "'";db.execSQL(sql);}return convertView;}class ViewHolder {TextView tv_1;ImageView iv;}}数据展示问题也解决了,那么 接下来就是删除事件,点击listview 或者长按listview中的item都有对应的监听事件分别是setOnItemClickListener、 setOnItemLongClickListener
删除事件的实现:
public void del(View view) {// 删除图片if (position != -1) {AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);builder.setTitle("刪除" + data.get(position).name).setMessage("此操作不可逆,是否继续?");// 相当于确定builder.setPositiveButton("确定",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {/** String sql = "delete from img where url='" +* data.get(position).url + "'"; db.execSQL(sql);*/Uri uri = Uri.parse("content://com.picture.provider");// 获取ContentResolverContentResolver cr = getContentResolver();// 准备数据cr.delete(uri, "url='" + data.get(position).url+ "'", null);File file = new File(data.get(position).path);if (file.exists()) { // 如果存在那么删除本地文件file.delete();}data = readDataBase(); // 读取数据库中的内容if (data.size() == 0) { // 没有东西那么接下的链接可以直接下载flag = true;MainActivity.this.position = -1;}adapter.notifyDataSetChanged();// 提示更新界面}});// 相当于取消builder.setNegativeButton("取消",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.show();} else {Toast.makeText(this, "当前没有选中任何图片!", Toast.LENGTH_LONG).show();}}
删除功能也实现了,接下来我们实现ContentProvider功能其实也简单,写一个类继承自ContentProvider
import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.util.Log;public class MyContentProvider extends ContentProvider {MySqliteHelper helper;@Overridepublic boolean onCreate() {helper = new MySqliteHelper(getContext());if (helper != null) {return true;}return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {SQLiteDatabase db = helper.getWritableDatabase();Cursor cursor = db.query("img", projection, selection, selectionArgs,null, null, sortOrder);cursor.setNotificationUri(getContext().getContentResolver(), uri); // 通知界面更新return cursor;}@Overridepublic String getType(Uri uri) {return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {Log.e("insert", " " + uri.getAuthority());SQLiteDatabase db = helper.getWritableDatabase();long id = -1;id = db.insert("img", null, values);db.close();getContext().getContentResolver().notifyChange(uri, null);return ContentUris.withAppendedId(uri, id); }@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {Log.e("delete", "delete");SQLiteDatabase db = helper.getWritableDatabase();int count = db.delete("img", selection, selectionArgs);getContext().getContentResolver().notifyChange(uri, null);return count; //返回删除的条数}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {Log.e("update", "update"+selection);SQLiteDatabase db = helper.getWritableDatabase();int count = db.update("img", values, selection, selectionArgs);getContext().getContentResolver().notifyChange(uri, null);return count;}}在xml中注册provider提供给其他程序
<provider android:name="com.wenxiangli.MyContentProvider"android:authorities="com.picture.provider"android:exported="true" ></provider>至此我们需要的功能都实现了。接下来说几个遇到的问题注意点:
1.权限一定不要忘记了
<!-- 联网权限 --><uses-permission android:name="android.permission.INTERNET" /><!-- SDcard的读写权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 读取Sdcard状态权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />2.为了完成第二次启动加载本地图片,我们应该先从数据库读出数据然后绑定到集合中去通知adapter更新
3.删除特别注意越界问题,这里我是通过每次删除结束设置positon为-1,当点击事件产生将改变position的值去判断是否删除。
4.保证图库中的数据唯一,所以每次下载前判断图片是否存在,存在就不去下载。直接提示是否更改文件的名字
下面附上完整源码地址需要的可以在这下载
链接:http://pan.baidu.com/s/1dEW5JBr 密码:m3jb
写这么长不容易啊,如果对你有帮助赏给回复 啊哈哈
总结
以上是生活随笔为你收集整理的简单图库软件的实现(联网下载图片保存到sdcard在Listview中展示,并作为ContentProvider为其他软件提供图库数据)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: SharedPreferences记住用
- 下一篇: 根据网络状态获取Ip地址