編輯:關於Android編程
前幾天寫了一個Android記事本小程序,現在記錄一下。
考慮到是記事本小程序,記錄的內容只有文字,而且內容不會太長,所以選擇使用SQLite數據庫,數據存放在用戶的手機上。
牽涉到數據庫,那自然是一個實體。先設計實體數據表:DBHelper.java
package com.ikok.notepad.DBUtil; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * Created by Anonymous on 2016/3/24. */ public class DBHelper extends SQLiteOpenHelper { /** * 創建筆記表 */ private static final String CREATE_NOTE = "create table Note(" + "id integer primary key autoincrement," + "content text," + "time text)"; private Context mContext; public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL(CREATE_NOTE); // Toast.makeText(mContext,"Created",Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
package com.ikok.notepad.DBUtil; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.ikok.notepad.Entity.Note; import java.util.ArrayList; import java.util.List; /** * Created by Anonymous on 2016/3/24. */ public class NoteDB { public static final String DB_NAME = "notepad"; public static final int VERSION = 1; private static NoteDB mNoteDB; private SQLiteDatabase db; public NoteDB(Context context) { DBHelper dbHelper = new DBHelper(context,DB_NAME,null,VERSION); db = dbHelper.getWritableDatabase(); } /** * 獲取 NoteDB 的實例 * @param context * @return */ public synchronized static NoteDB getInstance(Context context){ if (mNoteDB == null){ mNoteDB = new NoteDB(context); } return mNoteDB; } public void saveNote(Note note){ if (note != null) { ContentValues values = new ContentValues(); values.put("content", note.getContent()); values.put("time", note.getTime()); db.insert("Note", null, values); } } public ListloadNotes(){ List noteList = new ArrayList (); /** * 先按時間降序排列,再按id降序排列 */ Cursor cursor = db.query("Note",null,null,null,null,null,"time desc,id desc"); if (cursor.moveToNext()){ do { Note note = new Note(); note.setId(cursor.getInt(cursor.getColumnIndex("id"))); note.setContent(cursor.getString(cursor.getColumnIndex("content"))); note.setTime(cursor.getString(cursor.getColumnIndex("time"))); noteList.add(note); } while (cursor.moveToNext()); } return noteList; } public Note loadById(int id){ Note note = null; Cursor cursor = db.query("Note",null,"id = " + id,null,null,null,null); if (cursor.moveToNext()){ note = new Note(); note.setContent(cursor.getString(cursor.getColumnIndex("content"))); note.setTime(cursor.getString(cursor.getColumnIndex("time"))); } return note; } public void deleteById(Integer id){ db.delete("Note","id = " + id,null); } public void deleteAllNote(){ db.delete("Note", null, null); } public void updateById(String noteTime, String noteContent, int noteId){ ContentValues values = new ContentValues(); values.put("content",noteContent); values.put("time",noteTime); db.update("Note",values,"id = " + noteId,null); } }
設計完數據庫後,與數據庫對應的需要一個實體類:Note.java
package com.ikok.notepad.Entity; import java.io.Serializable; /** * Created by Anonymous on 2016/3/24. */ public class Note implements Serializable { private int id; private String content; private String time; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
具體效果如下(圖標懶得去改顏色了):
左邊的是一個關於App的按鈕,右邊的新建記事本的按鈕。
因為主頁需要顯示已經記錄的內容,所以我選擇用ListView去顯示。用到ListView,則與之對應的是要一個數據源,一個適配器。所以我為每一條子項設計了一個樣式,去讓它左邊顯示創建或更新的時間,右邊顯示內容。如下:list_item.xml
創建好了ListView,接下來為它准備適配器:MyAdapter.java
package com.ikok.notepad.Util; import android.content.Context; 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.ikok.notepad.Entity.Note; import com.ikok.notepad.R; import java.util.List; /** * Created by Anonymous on 2016/3/24. */ public class MyAdapter extends BaseAdapter { private ListnoteList; private LayoutInflater mInflater; private Context mContext; private int index; public MyAdapter(Context context,List noteList,ListView listView) { this.mInflater = LayoutInflater.from(context); this.noteList = noteList; this.mContext = context; } @Override public int getCount() { return noteList.size(); } @Override public Object getItem(int i) { return noteList.get(i); } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View convertView, ViewGroup viewGroup) { ViewHolder viewHolder = null; if (convertView == null){ viewHolder = new ViewHolder(); convertView = mInflater.inflate(R.layout.list_item, null); viewHolder.mTime = (TextView) convertView.findViewById(R.id.show_time); viewHolder.mContent = (TextView) convertView.findViewById(R.id.show_content); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.mTime.setText(noteList.get(i).getTime()); viewHolder.mContent.setText(noteList.get(i).getContent()); index = i; // convertView.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View view) { // Intent intent = new Intent(mContext,UpdateOrReadActivity.class); //// Bundle bundle = new Bundle(); //// bundle.putSerializable("note_item",noteList.get(index)); //// intent.putExtras(bundle); // intent.putExtra("note_id",noteList.get(index).getId()); // Log.d("Anonymous","備忘錄ID:"+noteList.get(index).getId()); // mContext.startActivity(intent); // Log.d("Anonymous","執行了適配器裡的點擊事件"); // } // }); return convertView; } class ViewHolder{ public TextView mTime; public TextView mContent; } }
創建好了ListView,准備好了適配器,接下來要為ListView准備數據源,而這數據源是要從數據庫讀出來的。但是數據庫操作和網絡訪問等都是屬於耗時操作,如果用主UI線程去執行響應操作的話,很可能會出現ANR現象,所以這裡我用AsyncTask去執行數據庫操作。主Activity代碼如下:MainActivity.java
package com.ikok.notepad.Activity; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.view.View; import android.view.Window; import android.widget.AdapterView; import android.widget.ImageButton; import android.widget.ListView; import android.widget.TextView; import com.ikok.notepad.DBUtil.NoteDB; import com.ikok.notepad.Entity.Note; import com.ikok.notepad.R; import com.ikok.notepad.Util.DeleteAsyncTask; import com.ikok.notepad.Util.MyAdapter; import java.util.ArrayList; import java.util.List; /** * Created by Anonymous on 2016/3/24. */ public class MainActivity extends Activity { /** * 布局控件 */ private TextView mTitle; private TextView mNoteNum; private ImageButton mWrite; private ListView mNoteListView; private ImageButton mAbout; /** * 數據庫實例,數據源 */ private ListmNoteList = new ArrayList () ; private NoteDB mNoteDB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main_activity); initView(); new NewAsyncTask().execute(); initEvent(); } private void initEvent() { /** * 新寫一條備忘錄 */ mWrite.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, AddNoteActivity.class); startActivity(intent); } }); /** * 修改或查看一條已有的備忘錄 */ mNoteListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { Note note = (Note) adapterView.getItemAtPosition(i); // Log.d("Anonymous", "點擊ListView獲取的note id: " + note.getId()); Intent intent = new Intent(MainActivity.this, UpdateOrReadActivity.class); intent.putExtra("note_id", note.getId()); startActivity(intent); } }); /** * listview長按刪除 */ mNoteListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { final Note note = (Note) parent.getItemAtPosition(position); // Log.d("Anonymous", "長按ListView獲取的note id: " + note.getId()); /** * 長按提示是否刪除 */ new AlertDialog.Builder(MainActivity.this) .setTitle("提示") .setMessage("真的要刪除這條記錄嗎?") .setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new DeleteAsyncTask(mNoteDB).execute(note.getId()); new NewAsyncTask().execute(); } }) .setNegativeButton("取消", null) .show(); return true; } }); /** * 關於自己 */ mAbout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,AboutActivity.class); startActivity(intent); } }); } public void initView() { /** * 布局控件初始化 */ mTitle = (TextView) findViewById(R.id.app_title); // 畫TextView文字下的下劃線 // mTitle.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG); mNoteNum = (TextView) findViewById(R.id.note_num); mWrite = (ImageButton) findViewById(R.id.write_btn); mNoteListView = (ListView) findViewById(R.id.listview); mAbout = (ImageButton) findViewById(R.id.about_btn); /** * 獲取數據庫實例 */ mNoteDB = NoteDB.getInstance(this); } /** * 異步加載備忘錄 */ class NewAsyncTask extends AsyncTask >{ @Override protected List doInBackground(Void... voids) { mNoteList = mNoteDB.loadNotes(); return mNoteList; } @Override protected void onPostExecute(List notes) { super.onPostExecute(notes); /** * 設置適配器,綁定適配器 */ MyAdapter myAdapter = new MyAdapter(MainActivity.this,notes,mNoteListView); mNoteListView.setAdapter(myAdapter); /** * 更新備忘錄記錄數 */ int temp = mNoteList.size(); mNoteNum.setText("共 " + temp + " 條備忘錄"); } } /** * 當活動恢復時,刷新listview和備忘錄記錄數 */ @Override protected void onResume() { super.onResume(); new NewAsyncTask().execute(); } }
接下來是新建記事本的Activity,布局如下:write_note.xml
具體效果如下:
新建記事本的Activity如下:AddNoteActivity.java
package com.ikok.notepad.Activity; import android.app.Activity; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.view.View; import android.view.Window; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import com.ikok.notepad.DBUtil.NoteDB; import com.ikok.notepad.Entity.Note; import com.ikok.notepad.R; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by Anonymous on 2016/3/24. */ public class AddNoteActivity extends Activity { /** * 布局控件 */ private TextView mComplete; private ImageButton mBackBtn; private EditText mContent; /** * 備忘錄數據 */ private String noteTime; private String noteContent; /** * 數據庫 */ private NoteDB mNoteDB; private Note note; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.write_note); initView(); initEvent(); } private void initView() { /** * 布局控件初始化 */ mComplete = (TextView) findViewById(R.id.complete_btn); mBackBtn = (ImageButton) findViewById(R.id.back_btn); mContent = (EditText) findViewById(R.id.note_content); /** * 獲取數據庫實例 */ mNoteDB = NoteDB.getInstance(this); } /** * 事件處理 */ private void initEvent() { /** * 返回上一級菜單,如果有內容,提示是否保存 * 是、保存,銷毀活動;否,直接銷毀活動 */ mBackBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { saveDataOrNot(); } }); /** * 完成按鈕,保存備忘錄到數據庫 */ mComplete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!mContent.getText().toString().equals("")){ new AddAsyncTask().execute(); finish(); } else { finish(); } } }); } /** * 根據是否有內容,提示保存 */ private void saveDataOrNot() { if (!mContent.getText().toString().trim().equals("")) { new AlertDialog.Builder(AddNoteActivity.this) .setTitle("提示") .setMessage("需要保存您編輯的內容嗎?") .setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new AddAsyncTask().execute(); finish(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .show(); } else { finish(); } } /** * 添加數據到數據庫 */ class AddAsyncTask extends AsyncTask{ @Override protected Void doInBackground(Void... voids) { mNoteDB.saveNote(note); return null; } @Override protected void onPreExecute() { super.onPreExecute(); /** * 記錄數據 */ SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm"); Date date = new Date(System.currentTimeMillis()); noteTime = sdf.format(date); noteContent = mContent.getText().toString(); note = new Note(); note.setTime(noteTime); note.setContent(noteContent); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Toast.makeText(AddNoteActivity.this, "保存成功!", Toast.LENGTH_SHORT).show(); } } /** * 按返回鍵,有內容時,提示保存 */ @Override public void onBackPressed() { saveDataOrNot(); } }
接下來是查看或修改一條記事本了,布局我是直接復用新建記事本的布局。因為沒有區別 - -
接下來是查看或修改一條記事本的Activity了,之前,我想的是點擊一條記事本,則進入這條記事本,把這條記事本直接顯示在頁面上,用戶直接在內容最後進行編輯。所以這裡需要一個子項點擊事件。我在MainActivity裡已經寫了,先獲取當前點擊的這一項的對象,這裡我費了好多時間,我不知道點擊這一項的時候,怎麼把該項的對象讀取出來。最後自己查看源碼,查API,看到參數中AdapterView是個泛型,我試著從它著手,把它強轉成Note對象,然後試試獲取id,沒想到就成了。 - -
所以,我獲取了當前點擊的item中的Note對象的id,把它放在Intent中,帶著這個參數去開啟活動。
這裡,查看或修改一條記事本的Activity正式開始了,如下:UpdateOrReadActivity.java
package com.ikok.notepad.Activity; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.View; import android.view.Window; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import com.ikok.notepad.DBUtil.NoteDB; import com.ikok.notepad.Entity.Note; import com.ikok.notepad.R; import com.ikok.notepad.Util.DeleteAsyncTask; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by Anonymous on 2016/3/24. */ public class UpdateOrReadActivity extends Activity { /** * 布局控件 */ private TextView mComplete; private ImageButton mBackBtn; private EditText mContent; private LinearLayout mScreen; /** * 備忘錄數據 */ private int noteId; private String noteTime; private String noteContent; private String originData; /** * 數據庫 */ private NoteDB mNoteDB; private static Note note; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.write_note); /** * 獲取傳遞過來的note對象 */ Intent intent = getIntent(); // 傳遞Note對象,必須要Note實體實現Serializable // note = (Note) intent.getSerializableExtra("note_item"); noteId = intent.getIntExtra("note_id",0); Log.d("Anonymous", "傳遞後的備忘錄ID:" + noteId); initView(); /** * 加載顯示數據 */ new LoadAsyncTask().execute(); initEvent(); } private void initView() { /** * 布局控件初始化 */ mComplete = (TextView) findViewById(R.id.complete_btn); mBackBtn = (ImageButton) findViewById(R.id.back_btn); mContent = (EditText) findViewById(R.id.note_content); mScreen = (LinearLayout) findViewById(R.id.screen_view); /** * 獲取數據庫實例 */ mNoteDB = NoteDB.getInstance(this); } private void initEvent() { /** * 返回上一級菜單,直接銷毀當前活動 */ mBackBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { updateDataOrNot(); } }); /** * 完成按鈕,修改備忘錄到數據庫 */ mComplete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mContent.getText().toString().trim().equals("")){ // Log.d("Anonymous","進入判斷為空函數"); new DeleteAsyncTask(mNoteDB).execute(noteId); finish(); } else if (mContent.getText().toString().equals(originData)) { finish(); } else { // Log.d("Anonymous","進入判斷不為空函數"); new UpdateAsyncTask().execute(); // Toast.makeText(UpdateOrReadActivity.this, "修改成功!", Toast.LENGTH_SHORT).show(); finish(); } } }); /** * 點擊屏幕空白區域,EditText選中 */ } /** * 根據id從數據庫讀數據的異步任務 */ class LoadAsyncTask extends AsyncTask{ @Override protected Note doInBackground(Void... voids) { note = mNoteDB.loadById(noteId); return note; } @Override protected void onPostExecute(Note note) { super.onPostExecute(note); /** * 根據傳遞進來的Note顯示備忘錄內容,並把光標移動到最後 * 記錄最初的文本內容 */ originData = note.getContent(); mContent.setText(note.getContent()); mContent.setSelection(mContent.getText().toString().length()); } } /** * 更新數據庫的異步任務 */ class UpdateAsyncTask extends AsyncTask { @Override protected void onPreExecute() { super.onPreExecute(); /** * 記錄數據 */ SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm"); Date date = new Date(System.currentTimeMillis()); noteTime = sdf.format(date); noteContent = mContent.getText().toString(); note.setTime(noteTime); note.setContent(noteContent); } @Override protected Void doInBackground(Void... voids) { mNoteDB.updateById(noteTime, noteContent, noteId); return null; } } /** * 根據是否有內容,提示保存 */ private void updateDataOrNot() { if (!mContent.getText().toString().equals(originData)) { new AlertDialog.Builder(UpdateOrReadActivity.this) .setTitle("提示") .setMessage("需要保存您編輯的內容嗎?") .setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new UpdateAsyncTask().execute(); finish(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .show(); } else { finish(); } } /** * 返回鍵事件 * 根據內容是否有變化,提示是否保存 */ @Override public void onBackPressed() { updateDataOrNot(); } }
操作數據庫還是用了AsyncTask。這裡,我考慮了,是否有改動,用一個變量,去存放原始的數據,在用戶點擊頂部返回或者系統返回鍵的時候去判斷是否有改動,如果有,則提示用戶是否需要保存更改。如果修改內容,沒有字了,則自動刪除該條記事本。因為刪除記事本的操作,在主頁還需要用到,所以我把它提出來,單獨作為一個類,不再是內部類了。如下:
package com.ikok.notepad.Util; import android.os.AsyncTask; import com.ikok.notepad.DBUtil.NoteDB; /** * Created by Anonymous on 2016/3/25. */ public class DeleteAsyncTask extends AsyncTask{ private NoteDB noteDB; public DeleteAsyncTask(NoteDB noteDB) { this.noteDB = noteDB; } @Override protected Void doInBackground(Integer... params) { noteDB.deleteById(params[0]); return null; } }
接下來是CRUD的最後一項,刪除數據了,在主頁的時候,我設計的是單擊進入該條記事本,去查看或修改這一條記事本,然後我考慮的是長按刪除。長按,彈出對話框,提示是否刪除,是則刪除,否則不做任何事。所以在MainActivity中可以看到長按事件的監聽器。但是因為Android的事件分發機制,長按事件必定會觸發點擊事件。所以需要在ListView中設置這樣一個屬性,才能點擊事件和長按事件同時監聽。
android:descendantFocusability="blocksDescendants"
主要功能都差不多完成了。接下來就是優化App了。我設計了過渡動畫,引導頁,以及是否第一次啟動App。是則過渡動畫過渡完到引導頁,引導頁完才到主頁。否則過渡動畫過渡完則直接進入主頁。還設計了引導頁的切換動畫,使用了nineoldandroid,保證動畫在低版本手機上可顯示。
廢話不多說,咱們第一篇文章就是模仿“知乎”的回答詳情頁的動畫效果,先上個原版的效果圖,咱們就是要做出這個效果 在實現之前,
零、Gallery的使用回顧我們有時候在iPhone手機上或者Windows上面看到動態的圖片,可以通過鼠標或者手指觸摸來移動它,產生動態的圖片滾動效果,還可以根據你的點
天天看高清影視片庫無法連接,不知道為什麼老是連接不上,下面是我總結了一下為什麼會這樣的問題,看能不能在裡面找到你們所需要的,幫你們解決天天看高清影視出現的問
一、自定義控件(一) --- 自定義屬性TextView1,定義屬性,制作attrs.xml文件;屬性值:string,color,attr,array,bool,dec