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) { } }
创建完数据表后,自然需要操作数据库,crud数据,我把所有跟数据库有关的操作封装在一起:notedb.java
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 list<note> loadnotes(){ list<note> notelist = new arraylist<note>(); /** * 先按时间降序排列,再按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主页的设计:main_activity.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/repeat_bg" android:orientation="vertical"> <textview android:id="@+id/app_title" android:layout_width="match_parent" android:layout_height="40dp" android:textsize="16sp" android:gravity="center" android:text="@string/app_title" android:textcolor="#333" /> <listview android:id="@+id/listview" android:descendantfocusability="blocksdescendants" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </listview> <relativelayout android:layout_width="match_parent" android:layout_height="40dp" > <imagebutton android:id="@+id/about_btn" android:src="@drawable/about_me" android:layout_alignparentleft="true" android:paddingleft="20dp" android:background="#00ffffff" android:scaletype="center" android:layout_margintop="4dp" android:layout_width="52dp" android:layout_height="32dp" /> <textview android:id="@+id/note_num" android:layout_width="wrap_content" android:layout_height="30dp" android:paddingtop="2dp" android:textsize="18sp" android:textcolor="#333" android:layout_centerinparent="true" android:text="@string/app_title" /> <imagebutton android:id="@+id/write_btn" android:src="@drawable/write_btn" android:layout_alignparentright="true" android:paddingright="20dp" android:background="#00ffffff" android:scaletype="center" android:layout_margintop="4dp" android:layout_width="52dp" android:layout_height="32dp" /> </relativelayout> </linearlayout>
具体效果如下(图标懒得去改颜色了):
左边的是一个关于app的按钮,右边的新建记事本的按钮。
因为主页需要显示已经记录的内容,所以我选择用listview去显示。用到listview,则与之对应的是要一个数据源,一个适配器。所以我为每一条子项设计了一个样式,去让它左边显示创建或更新的时间,右边显示内容。如下:list_item.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:minheight="50dp" android:orientation="horizontal"> <textview android:id="@+id/show_time" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:paddingleft="10dp" android:textcolor="#333" android:textsize="16sp" /> <textview android:id="@+id/show_content" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:textsize="16sp" android:paddingleft="20dp" android:textcolor="#333" android:paddingtop="14dp" android:singleline="true" /> </linearlayout>
创建好了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 list<note> notelist; private layoutinflater minflater; private context mcontext; private int index; public myadapter(context context,list<note> 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; } }
这里采用了使用viewholder,来使listview滚动的时候不必每次重新创建对象,提升性能。
创建好了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 list<note> mnotelist = new arraylist<note>() ; 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<void,void,list<note>>{ @override protected list<note> doinbackground(void... voids) { mnotelist = mnotedb.loadnotes(); return mnotelist; } @override protected void onpostexecute(list<note> 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(); } }
在上面的代码中,我新建了一个 newasynctask 类去继承 asynctask,去执行从数据库读取数据的操作,在onpostexecute()方法中,去更新ui,比如显示listview中的数据,一下页面底部中间有几条数据等。还有我考虑了新建记事本的话,是另外一个activity。当从另外的activity返回到主activity时,主页面应该再刷新一次,刷新数据和显示,所以我在onresume()方法中调用了 newasynctask().execute() 方法,当活动恢复时刷新显示。
接下来是新建记事本的activity,布局如下:write_note.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/screen_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/repeat_bg" android:orientation="vertical"> <relativelayout android:layout_width="match_parent" android:layout_height="40dp"> <imagebutton android:id="@+id/back_btn" android:src="@drawable/back_btn" android:layout_alignparentleft="true" android:paddingleft="5dp" android:background="#00ffffff" android:scaletype="center" android:layout_margintop="6dp" android:layout_width="52dp" android:layout_height="32dp" /> <textview android:id="@+id/complete_btn" android:layout_alignparentright="true" android:paddingtop="10dp" android:paddingright="10dp" android:textsize="18sp" android:textcolor="#ec6d51" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/complete"/> </relativelayout> <edittext android:id="@+id/note_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingleft="10dp" android:paddingright="10dp" android:textcolor="#333" android:textcursordrawable="@null" android:background="@null"/> </linearlayout>
具体效果如下:
新建记事本的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<void,void,void>{ @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(); } }
新建记事本,插入数据到数据库,如从数据库读取数据一样,都是耗时操作,所以我还是用了asynctask,在 onpreexecute()方法中,先获取到系统当前时间,进行格式化,存储下来,把输入的文本存储下来,然后再 doinbackground()去保存数据。这里我考虑了,用户输入了内容,但是没有保存,在顶部的返回键或者系统的返回键的处理事件中都加了判断。如果文本为空,空格也算空,则不保存,直接退出当前activity,如果有内容,则弹出对话框提示用户是否保存,是则保存,否则不保存,退出当前活动。
接下来是查看或修改一条记事本了,布局我是直接复用新建记事本的布局。因为没有区别 - -
接下来是查看或修改一条记事本的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<void,void,note>{ @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<void,void,void>{ @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<integer,void,void> { 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,保证动画在低版本手机上可显示。
优化app部分可见我另外一篇博客,传送门:android实现过渡动画、引导页 android判断是否第一次启动app
项目地址在:https://github.com/someonexiaole/android
notepad 即是。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)
-
Android开发实现圆形图片功能示例
-
Android开发之获取单选与复选框的值操作示例
-
Android开发之android_gps定位服务简单实现
-
Android开发实现的圆角按钮、文字阴影按钮效果示例
-
Android开发经验谈:并发编程(线程与线程池)(推荐)
-
Android开发之TextView使用intent传递信息,实现注册界面功能示例
-
Android开发中TextView各种常见使用方法小结
-
iOS开发中常见的项目文件与MVC结构优化思路解析
-
谷歌意外上线Android 11开发者预览版网页:随后撤下