简单图库软件的实现(联网下载图片保存到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记住用
- 下一篇: Android Studio 中如何引入