概述:这个小程序,你讲学习到基本控件(Button,Listview,Gridview,TextView等)的使用技巧,AssetManager类的使用,XML数据的解析方式,BaseAdapter,几种布局的使用,Sqlite的使用等等。

一、简单需求设计

左右分栏的形式,左栏为一级菜单,右栏为二级菜单。通过点击一级菜单列表在右栏现实二级菜单,在二级菜单中点击相关选项进入网页。一级菜单,二级菜单均有编辑功能,或增,或删,或改。为提高用户体验,将二级菜单的显示模式定为两种,一为列表模式,二为表格方式。将在一二级菜单中添加列表头或者列表尾进行添加操作。为更迎合用户的偏好使用,将设计将用户本次的设置保存到系统中,方便用户下次使用。提供恢复默认设置的选项,即将数据初始为原始状态。

二、技术实现可能性预见

就我本人而言,做过的项目不多,那么首先我会做一个简单的技术考察,或做一些小Demo。那么这里,我就以我的方式来阐述下这个项目需要用到的一个技术。首先,必然要用到xml的解析,那么xml有三种解析方式(这里只做简单说明):(1)DOM解析,Dom解析器会在解析xml文档是,把文档中所有的元素按照其出现的层次关系解析成一个个Node节点对象,Node节点对象提供了相应的方法去获取父节点和子节点,那么我们可以通过这些方法读取整个xml并对其操作,它的缺点是一次性加载整个xml文件,那么对于移动设备,内存是非常宝贵的,所以我们不用它。(2)SAX解析,SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档撞在完毕才对其进行操作。它是基于事件驱动的(3)PULL方式,和SAX一样也是基于事件驱动的,使用起来也比较简单,那么我们就用Pull。Activity之间的数据传递和保存,这个是完全可以处理的。调用相应的方法,或者将需要存储的数据保存到sqlite就可以了。这些开始开代码吧。功能的实现,基本没有什么问题了,那么下一步我们就来布局下界面。

三、界面布局的设计

根据之前的需求设计,大致将界面布局定为以下样式。

四、项目关键技术点讲解及代码展示

1、在Eclipse中新建工程,SurfingDemo,程序包名:org.winplus.sufing

2、按照我的编码习惯,我喜欢先将UI弄好,然后直接取数据,并将其显示。那么根据上图UI的设计草图,我们可以将几个主要的布局进行排版设计。代码如下:

surfing.xml(布局描述文件)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="horizontal" >

<!-- 左边的操作布局即,一级菜单的显示-->

<LinearLayout

android:id="@+id/layout_left"

android:layout_width="200px"

android:layout_height="wrap_content"

android:orientation="vertical" >

<TextView

android:id="@+id/left_msg"

android:layout_width="wrap_content"

android:layout_height="40px"

android:text="@string/txt_left_msg" />

<ListView

android:id="@+id/list_first_classify"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

</LinearLayout>

<!-- 右边布局,即二级菜单的显示-->

<LinearLayout

android:id="@+id/layout_right"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_weight="1"

android:orientation="vertical" >

<!-- 右边操作布局,显示模式的设置-->

<LinearLayout

android:id="@+id/layout_right_oper"

android:layout_width="fill_parent"

android:layout_height="40px"

android:orientation="horizontal" >

<TextView

android:id="@+id/right_msg"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_weight="1"

android:text="@string/txt_right_msg" />

<ImageButton

android:id="@+id/display_list"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="#00000000"

android:src="@drawable/ic_display_list"/>

<ImageButton

android:id="@+id/display_grid"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="#00000000"

android:src="@drawable/ic_display_grid"/>

</LinearLayout>

<!-- 二级菜单的主显示-->

<FrameLayout

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_weight="1" >

<ListView

android:id="@+id/second_classify_list"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

<GridView

android:id="@+id/second_classify_grid"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:numColumns="auto_fit"

android:verticalSpacing="5dp"

android:horizontalSpacing="5dp"

android:columnWidth="80dp"

android:stretchMode="columnWidth"

android:gravity="center"/>

</FrameLayout>

</LinearLayout>

</LinearLayout>

</span></span>

界面布局完成了,下一步我们先将一些数据整理出来,我把数据整理成xml文档,供程序初始化使用,数据文档如下:

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>

<classify>

<!--视频(奇异高清,QQ视频,优酷,土豆,新浪视频)-->

<subclassify name="视频" icon="img/video.png">

<url_data id="1" name="奇异高清" icon="img/vqiyi.png" url="http://www.qiyi.com" describe=""/>

<url_data id="1" name="QQ视频" icon="img/vqq.png" url="http://www.v.qq.com" describe=""/>

<url_data id="1" name="优酷" icon="img/vyouku.png" url="http://www.youku.com" describe=""/>

<url_data id="1" name="土豆" icon="img/vtudou.png" url="http://www.tudou.com" describe=""/>

<url_data id="1" name="新浪视频" icon="img/vsina.png" url="http://video.sina.com.cn/" describe=""/>

</subclassify>

<!-- 新闻(新浪新闻,新华网,腾讯新闻,网易新闻,中国新闻网) -->

<subclassify name ="新闻" icon="img/news.png">

<url_data id="2" name="新浪新闻" icon="img/nsina.png" url="http://news.sina.com.cn/" describe=""/>

<url_data id="2" name="新华网" icon="img/nxinhua.png" url="http://www.xinhuanet.com/" describe=""/>

<url_data id="2" name="腾讯新闻" icon="img/nqq.png" url="http://news.qq.com/" describe=""/>

<url_data id="2" name="网易新闻" icon="img/n163.png" url="http://news.163.com/" describe=""/>

<url_data id="2" name="中国新闻网" icon="img/nchina.png" url="http://www.chinanews.com/" describe=""/>

</subclassify>

<!-- 军事(中华军事,铁血网,环球军事,新浪军事,东方军事)-->

<subclassify name ="军事" icon="img/war.png">

<url_data id="3" name="中华军事" icon="img/wchina.png" url="http://military.china.com/zh_cn/" describe=""/>

<url_data id="3" name="铁血网" icon="img/wtiexue.png" url="http://www.tiexue.net/" describe=""/>

<url_data id="3" name="环球军事" icon="img/whuanqiu.png" url="http://mil.huanqiu.com/" describe=""/>

<url_data id="3" name="新浪军事" icon="img/wsina.png" url="http://mil.news.sina.com.cn/" describe=""/>

<url_data id="3" name="东方军事" icon="img/weast.png" url="http://mil.eastday.com/" describe=""/>

</subclassify>

</classify>

</span></span>

当然还有图片资源,资源图片我就不提供了。对此项目感兴趣的就自己PS或者直接到网站去截图吧。

根据上面的数据显示,我定义了两个bean,来保存数据。

UrlClassify.java(用于保存subclassify的Name和Icon)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">package org.winplus.sufing;

public class UrlClassify {

private int id;

private String name;

private byte[] icon;

//getter setter method

}

</span></span>

UrlInfo.java(用于保存url_data标签的相关属性)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">public class UrlInfo {

private int fid;

private String name;

private byte[] icon;

private String url;

private String describe;

//getter setter method

}

</span></span>

OK,实体Bean定义好了,现在可以解析xml数据了。上面的xml数据被我定义为urldata.xml并将其放在Android工程目录assets下,通过AssetManager管理。要解析xml数据,就要用到上面提到的3中方法,这里我们使用Pull(你可以使用以上任意一种处理方式)。为了程序更有扩展性,我们将处理xml数据的方法写成一个接口,然后再通过实现接口去真正解析xml数据。直接看代码。

接口类很简单,就定义了一个接口:

IxmlParseService.java(解析XMl数据的接口类)

<span style="font-family: 'Microsoft YaHei'; font-weight: normal; "><span style="font-size:16px;">public interface IXmlParseService {

public Map<UrlClassify,ArrayList<UrlInfo>> getUrlsByParseXml(InputStream is) throws Exception;

}

PullParseService.java(实现解析数据的接口类),以下是主要方法

public Map<UrlClassify,ArrayList<UrlInfo>> getUrlsByParseXml(InputStream is)

throws Exception {

Map<UrlClassify,ArrayList<UrlInfo>> map = null; // 定义一个Map用于保存一级菜单和二级菜单数据(根据xml的结构,将一级菜单UrlClassify作为键值保存在Map中,UrlInfo数字列表保存为Map的值)

ArrayList<UrlInfo> classifies = null; // 定义保存UrlInfo的数组列表。

UrlClassify urlClassify = null;

UrlInfo urlInfo = null;

XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); // 得到一个PullParserFactory实例,用于创建Pull解析器

XmlPullParser parser = factory.newPullParser(); // 创建解析器

parser.setInput(is,"UTF-8"); // 设置解析格式

int type = parser.getEventType(); // 得到当前事件的类型(文档开始、结束,标签开始结束)

byte[] by = null; // 图片文件

while (type!=XmlPullParser.END_DOCUMENT) { // 当前事件类型为文档结束不再循环

String typeName = parser.getName();

if (type==XmlPullParser.START_TAG) {

if ("classify".equals(typeName)) {

map = new LinkedHashMap<UrlClassify, ArrayList<UrlInfo>>();

}else if ("subclassify".equals(typeName)) {

by = imgFile2Byte(parser.getAttributeValue(1));

urlClassify = new UrlClassify(parser.getAttributeValue(0), by);

classifies = new ArrayList<UrlInfo>();

}else if("url_data".equals(typeName)){

by = imgFile2Byte(parser.getAttributeValue(2));

int fid = Integer.valueOf( parser.getAttributeValue(0));

String name = parser.getAttributeValue(1);

String url = parser.getAttributeValue(3);

String describe = parser.getAttributeValue(4);

urlInfo = new UrlInfo(fid,name,by,url,describe);

classifies.add(urlInfo);

}

}else if (type == XmlPullParser.END_TAG) {

map.put(urlClassify, classifies);

}

type = parser.next();

}

return map;

}

</span></span>

解析xml的方法搞定,解析出来的数据是显示在listview中的,我们通过listview.setAdapter()方法设置数据,于是就需要先定义一个继承Adapter的类。在定义Adapter类之前,我们又有必要设计好在listview显示的布局描述文件(当然你也可以使用系统自带的布局)。在这个项目中,我并不打算花太多的精力去做界面上的美化工作,只实现基本的功能,或者演示基本的使用情况。我将一级菜单数据显示的UI定义成如下的样式。

根据上面的显示,定义了如下文件first_classify_adapter.xml(一级菜单Adapter布局文件)

<span style="font-family: 'Microsoft YaHei'; font-weight: normal; "><span style="font-size:16px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="horizontal" >

<ImageView

android:id="@+id/edit"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:visibility="gone"

android:src="@drawable/ic_edit" />

<ImageView

android:id="@+id/img"

android:layout_width="80dip"

android:layout_height="40dip" />

<TextView

android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_weight="1" />

</LinearLayout>

</span></span>

细心的读者会注意到,上面的布局文件中有一个隐藏的“编辑按钮”图标,非编辑情况下这个图标时隐藏不显示的,只有在编辑模式下才将图标显示。这个编辑功能应该是这样,在编辑模式下,点击编辑图片按钮,然后跳转到相应的Activity中,编辑当前项,然后将编辑过的项更新到数据库或者文件中,然后再返回当前的界面。为了减少对数据库的操作,我并没有打算更新数据后,再查一次数据库中的数据,然后在显示在界面。而且请注意:这个描述文件是在Adapter中使用的,我们都知道诸如startActivityForResult,startActivity(intent)是Activity中的方法,但在Adapter中怎么使用呢?没办法,我只有将SurfingDemoActivity通过构造函数传递到Adapter中,来实现引用,下面是关键代码。

FirstClassifyAdapter.java(一级列表的Adapter)

<span style="font-size:16px;">/**

* 以下的关于显示的处理方式是googleIO大会提出的优化方式之一,推荐使用这种方式!

*/

@Override

public View getView(int position, View convertView, ViewGroupparent) {

ViewHolder holder = null;

if (convertView == null) {

holder = new ViewHolder();

convertView = inflater.inflate(R.layout.first_classify_adapter, null);

holder.img = (ImageView) convertView.findViewById(R.id.img);

holder.text = (TextView) convertView.findViewById(R.id.text);

holder.imgEdit = (ImageView) convertView.findViewById(R.id.edit);

convertView.setTag(holder); // 这点很关键,当初我就是忘记了设置Tag,导致整个列表值缓存了一屏数据,当数据超过一屏数据时就重复显示了。在网上也看到很多哥们关于重复显示的迷惑。

} else {

holder = (ViewHolder) convertView.getTag();

}

final FirstClassify classify = mClassifies.get(position);

byte[] by = classify.getIcon();

if (by != null) {

ByteArrayInputStream bais = new ByteArrayInputStream(classify.getIcon());

holder.img.setImageDrawable(Drawable.createFromStream(bais,String.valueOf(position)));

} else {

holder.img.setBackgroundResource(R.drawable.ic_launcher);

}

holder.text.setText(classify.getName());

if (isEdit) {

holder.imgEdit.setVisibility(View.VISIBLE);

holder.imgEdit.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

Log.i(TAG, "classify.getName=" + classify.getName());

Intent intent = new Intent();

intent.setClassName("org.winplus.sufing","org.winplus.sufing.FirstClassifyEditActivity");

Bundle bundle = new Bundle();

bundle.putSerializable("classify", classify);

intent.putExtras(bundle);

mActivity.startActivityForResult(intent,

Constant.REQUEST_EDIT_CLASSIFY);

}

});

} else {

holder.imgEdit.setVisibility(View.GONE);

}

return convertView;

}

class ViewHolder {

public ImageView img;

public TextView text;

public ImageView imgEdit;

}</span>

当然还有二级分类的显示Adapter,二级分类显示又有两种(ListView,GridView)显示方式,这里就省略了,实现方式和上面FirstClassifyAdapter类一样。

完成了上面的工作之后,我们终于可以将数据显示到界面上了!在自动生成的类SurfingDemoActivity中,先定义并初始化xml文件中相关的各个组件。从上面的需求以及surfing.xml描述文件中我们看到了,这个类中需要实现几个必须得事件监听类。当然也可以不通过实现的方式,直接重写控件的方法,这样如果控件多,实现起来就比较冗余,个人习惯吧。这个类中我们通过实现OnClickListener,OnItemClickListener,OnItemLongClickListener三个类来处理ListView的点击、长按,以及按钮的点击事件。

这个类中,加载数据的流程是这样,先判断是否是第一次运行,如果是第一次运行则解析xml数据,并将其保存到数据库,然后再显示在界面。如果非第一次运行,则直接查询数据库中的数据,然后显示。SurfingDemoActivity.java这个类中关键代码如下。

SurfingDemoActivity.java(代码中都有注释,这里就不说了。)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">/**

* 初始化数据(判断程序是否是第一次运行,如果是第一次运行则将数据加载到数据库中),关于是否第一次运行的判断,我是直接通过SharedPreferences,将数据保存在共享文件中,如果读取到的值为false表示第一次运行。第一次运行后设置共享文件中的值为true,即可实现。这个好像没有什么好的实现方式吧?

*/

private void init() {

if (!utils.isFirstRunning()) {

map = new LinkedHashMap<FirstClassify, ArrayList<UrlInfo>>();

// TODO: 加线程

initXML();

initData();

utils.setFirstRuslt();

}

setDefaultData(false);

getSecondDisplay();

}

/**

* 解析数据

*/

private void initXML() {

InputStream stream = null;

AssetManager manager = getAssets();

try {

stream = manager.open("urldata.xml");

} catch (IOException e) {

Log.i(TAG, e.getMessage());

}

try {

map = new PullParseService(manager).getUrlsByParseXml(stream);

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 将一级分类和二级分类的数据加载到数据库中

*/

private void initData() {

ArrayList<UrlInfo> urlInfos = new ArrayList<UrlInfo>();

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()) {

HashMap.Entry entry = (HashMap.Entry)iter.next();

FirstClassify classify = (FirstClassify) entry.getKey();

urlInfos.addAll((ArrayList<UrlInfo>)entry.getValue()); // 二级分类添加到列表

mDbHelper.insertClassify(classify);// 将一级分类的数据保存在数据中

}

int count = urlInfos.size();

for (int i = 0; i < count; i++) {

UrlInfo urlInfo = urlInfos.get(i);

mDbHelper.insertUrlInfo(urlInfo);

Log.i(TAG, "to dburlInfo.name=" + urlInfo.getName());

}

}

/**

* 设置默认显示的数据

*/

private void setDefaultData(boolean editMode) {

Log.i(TAG, "setDefaultData");

mClassifies = mDbHelper.queryClassify();

getFirstClassifyDisplay(editMode);

int def = utils.getDefaultLoading();

mUrlInfos = mDbHelper.queryUrlInfo(def);

if (mUrlInfos.isEmpty()) {

mUrlInfos = mDbHelper.queryUrlInfo(1);

}

getSecondDisplay();

}

/**

* 一级菜单显示

*

* @param editMode 是否编辑

*/

private void getFirstClassifyDisplay(boolean editMode) {

FirstClassifyAdapteradapter = new FirstClassifyAdapter(mClassifies,

mContext, isEdit, this);

Log.i(TAG, "adapter.count=" + adapter.getCount());

lstFirstClassify.setAdapter(adapter);

adapter.notifyDataSetChanged();

}

/**

* 得到默认设置的显示方式

*/

private void getSecondDisplay() {

switch (utils.getDisplayStyle()) {

case Constant.GRID_DISPLAY:

SecondClassifyGridAdapter secondClassifyGridAdapter = newSecondClassifyGridAdapter(mUrlInfos, mContext);

gidSecondClassify.setAdapter(secondClassifyGridAdapter);

lstSecondClassify.setVisibility(View.GONE);

gidSecondClassify.setVisibility(View.VISIBLE);

secondClassifyGridAdapter.notifyDataSetChanged();

break;

default:

SecondClassifyListAdapter secondClassifyAdapter = newSecondClassifyListAdapter(mUrlInfos, mContext);

lstSecondClassify.setAdapter(secondClassifyAdapter);

lstSecondClassify.setVisibility(View.VISIBLE);

gidSecondClassify.setVisibility(View.GONE);

secondClassifyAdapter.notifyDataSetChanged();

break;

}

}

/**

* 打开浏览器

* @param url

*/

private void openBrowser(String url){

Intent intent = new Intent();

intent.setAction("android.intent.action.VIEW");

Uri content_uri_browsers = Uri.parse(url);

intent.setData(content_uri_browsers);

intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");

startActivity(intent);

}

</span></span>

对于列表的点击功能,上面我们说过通过实现OnItemClickListener,重写其中的方法

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">@Override

public void onItemClick(AdapterView<?> parent, View view, int position,

long id) {

UrlInfo urlInfo;

switch (parent.getId()) {

case R.id.list_first_classify:

FirstClassify firstClassify = (FirstClassify) lstFirstClassify.getItemAtPosition(position); // 得到当前点击的对象,这句代码关键

txtRightMsg.setText(firstClassify.getName());

mUrlInfos = mDbHelper.queryUrlInfo(firstClassify.getId());

getSecondDisplay();

break;

case R.id.second_classify_list:

urlInfo = (UrlInfo) lstFirstClassify.getItemAtPosition(position);

openBrowser(urlInfo.getUrl());

break;

case R.id.second_classify_grid:

Log.i(TAG, "grid_context");

urlInfo = (UrlInfo) gidSecondClassify.getItemAtPosition(position);

openBrowser(urlInfo.getUrl());

break;

default:

break;

}

}

</span></span>

细心的读者又发现了,上面提到主要的方法中我们有调用到DBHelper类中相关的方法。是的,DBHelper是数据库相关操作的类,这里我列出来,给大家做个参考,类有待优化,还请各位别拍砖,呵呵

DBHelper.java(Sqlite数据库操作类)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">package org.winplus.surfing;

import java.util.ArrayList;

import android.R.integer;

import android.content.ContentValues;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.database.sqlite.SQLiteDatabase.CursorFactory;

import android.util.Log;

public class DBHelper {

private static final String TAG = "DBHelper";

public static final String ID = "_id";

public static final String NAME = "_name";

public static final String URL = "_url";

public static final String ICON = "_icon";

public static final String DESCRIBE = "_describe";

public static final String FKEY = "_fkey";

public static final String FID = "_id";

public static final String FNAME = "_name";

public static final String FICON = "_icon";

public static final int VERSION = 1;

public static final String DATABASE_NAME = "surfing.db";

public static final String TABLE_NAME_URLINFO = "urldata";

public static final String TABLE_NAME_CLASSIFY = "classify";

private static final String CREATE_TABLE_URLINFO = "create table "

+ TABLE_NAME_URLINFO + "(" + ID

+ " integer primary key autoincrement," + NAME + " text not null,"

+ URL + " text not null," + ICON + " blob," + DESCRIBE + "  text,"

+ FKEY + " integer)"; // 创建二级菜单关于URLinfo的表

private static final String CREATE_TABLE_CLASSFIY = "create table "

+ TABLE_NAME_CLASSIFY + "(" + FID

+ " integer primary key autoincrement," + FNAME + " text," + FICON

+ " blob)"; // 创建一级菜单数据表

private SQLiteDatabase sqldb;

private Context mContext;

private ContentValues values;

private SurfingSQLHelper sqlHelper;

public DBHelper(Context context) {

mContext = context;

}

/**

* 一个帮助类,用于创建和对数据库的版本进行管理

* @author acer

*

*/

class SurfingSQLHelper extends SQLiteOpenHelper {

public SurfingSQLHelper() {

// 创建数据库和数据库版本号

super(mContext, DATABASE_NAME, null, VERSION);

}

/**

* 此方法在第一次创建数据库的时候调用,一般我们用它来创建表

*/

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_TABLE_URLINFO);

db.execSQL(CREATE_TABLE_CLASSFIY);

}

@Override

public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {

// TODO Auto-generated method stub

db.execSQL("drop table if exists " + CREATE_TABLE_URLINFO);

db.execSQL("drop table if exists " + CREATE_TABLE_CLASSFIY);

onCreate(db);

}

}

/**

* 打开数据库

*/

public void open() {

sqlHelper = new SurfingSQLHelper();

sqldb = sqlHelper.getWritableDatabase();

}

/**

* 关闭数据库

*/

public void close() {

sqlHelper.close();

}

/**

* 添加网址信息

*

* @param urlInfo

* @return

*/

public int insertUrlInfo(UrlInfo urlInfo) {

open();

values = new ContentValues();

values.put(NAME, urlInfo.getName());

values.put(URL, urlInfo.getUrl());

values.put(ICON, urlInfo.getIcon());

values.put(DESCRIBE, urlInfo.getDescribe());

values.put(FKEY, urlInfo.getFid());

// empty是表中没有指向的列时用empty代替

int i = (int) sqldb.insert(TABLE_NAME_URLINFO, "empty", values);

close();

return i;

}

/**

* 添加一级分类记录

*

* @param classify

* @return

*/

public int insertClassify(FirstClassify classify) {

open();

Log.i(TAG, "insertClassify");

values = new ContentValues();

values.put(FNAME, classify.getName());

Log.i(TAG, "classify.getIcon()=" + (classify.getIcon() == null));

values.put(FICON, classify.getIcon());

// empty是表中没有指向的列时用empty代替

int i = (int) sqldb.insert(TABLE_NAME_CLASSIFY, "empty", values);

close();

return i;

}

/**

* 删除UrlInfo信息

*

* @param id

* @return

*/

public int deleteUrlInfo(int id) {

open();

String[] whereArgs = new String[] { String.valueOf(id) };

int i = sqldb.delete(TABLE_NAME_URLINFO, "_id=?", whereArgs);

close();

return i;

}

/**

* 根据以及分类的ID删除其所有子类

*

* @param fId

* @return

*/

public int deleteUrlInfoFid(int fId) {

open();

String[] whereArgs = new String[] { String.valueOf(fId) };

int i = sqldb.delete(TABLE_NAME_URLINFO, "_fkey=?", whereArgs);

close();

return i;

}

/**

* 删除Classify信息

*

* @param id

* @return

*/

public int deleteClassify(int id) {

open();

String[] whereArgs = { String.valueOf(id) };

int i = sqldb.delete(TABLE_NAME_CLASSIFY, "_id=?", whereArgs);

close();

return i;

}

/**

* 根据ID修改网址记录

*

* @param urlInfo

* @return

*/

public int updateUrlInfo(UrlInfo urlInfo) {

open();

values = new ContentValues();

values.put(NAME, urlInfo.getName());

values.put(URL, urlInfo.getUrl());

values.put(ICON, urlInfo.getIcon());

values.put(DESCRIBE, urlInfo.getDescribe());

values.put(FKEY, urlInfo.getFid());

String[] whereArgs = { String.valueOf(urlInfo.getFid()) };

int i = sqldb.update(TABLE_NAME_URLINFO, values, ID + "=?", whereArgs);

close();

return i;

}

/**

* 根据ID修改分类记录

*

* @param classify

* @return

*/

public int updateClassify(FirstClassify classify) {

open();

values = new ContentValues();

values.put(FNAME, classify.getName());

values.put(FICON, classify.getIcon());

String[] whereArgs = { String.valueOf(classify.getId()) };

int i = sqldb.update(TABLE_NAME_CLASSIFY, values, ID + "=?", whereArgs);

return i;

}

/**

* 根据ID查询UrlInfo

*

* @return

*/

public ArrayList<UrlInfo> queryUrlInfo(int id) {

open();

String[] whereArgs = new String[] { String.valueOf(id) };

Cursor cursor = sqldb.query(TABLE_NAME_URLINFO, null, "_fkey=?",

whereArgs, null, null, null, null);

ArrayList<UrlInfo> urlInfos = new ArrayList<UrlInfo>();

int count = cursor.getCount();

for (int i = 0; i < count; i++) {

cursor.moveToPosition(i);

UrlInfo urlBean = new UrlInfo();

urlBean.setFid(cursor.getInt(cursor.getColumnIndex(ID)));

urlBean.setName(cursor.getString(cursor.getColumnIndex(NAME)));

urlBean.setUrl(cursor.getString(cursor.getColumnIndex(URL)));

urlBean.setIcon(cursor.getBlob(cursor.getColumnIndex(ICON)));

urlBean.setDescribe(cursor.getString(cursor

.getColumnIndex(DESCRIBE)));

urlInfos.add(urlBean);

}

close();

return urlInfos;

}

/**

* 查询所有的Url分类

*

* @return

*/

public ArrayList<FirstClassify> queryClassify() {

open();

Cursor cursor = sqldb.query(TABLE_NAME_CLASSIFY, null, null, null,

null, null, null);

ArrayList<FirstClassify> classifies = new ArrayList<FirstClassify>();

int count = cursor.getCount();

for (int i = 0; i < count; i++) {

cursor.moveToPosition(i);

FirstClassify classify = new FirstClassify();

classify.setId(cursor.getInt(cursor.getColumnIndex(FID)));

classify.setName(cursor.getString(cursor.getColumnIndex(FNAME)));

classify.setIcon(cursor.getBlob(cursor.getColumnIndex(FICON)));

classifies.add(classify);

}

close();

return classifies;

}

}

</span></span>

Ok,项目写到这里,主要功能差不多完成了。当然还有很多需要优化的细节,比如说图片,比如说代码、UI优化工作,先上效果图看看(这只是初步的实现主要功能)

下面,我们再SurfingDemoActivity.java中添加一个Menu,首先在res目录下添加一个名为menu的文件夹,再新建一个名为surfing_menu.xml的xml文件。

surfing_menu.xml(Menu)

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

<item

android:id="@+id/menu_add"

android:icon="@drawable/ic_launcher"

android:title="添加"/>

<item

android:id="@+id/menu_edit"

android:icon="@drawable/ic_launcher"

android:title="编辑"/>

<item

android:id="@+id/menu_default"

android:icon="@drawable/ic_launcher"

android:title="恢复默认"/>

</menu>

</span></span>

在android中实现Menu的功能很简单,只需要重写onCreateOptionsMenu方法就好了,点击菜单执行相应的操作重写:onOptionsItemSelected。这里,我直接引用上面的xml文件来实现menu的布局。当然你可以直接在代码里面写。引用也很简单,就一句话:

<span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">this.getMenuInflater().inflate(R.menu.surfing_menu, menu);</span></span>

上面我们提到在菜单中有编辑功能。比如说添加一级菜单,二级菜单,这些都是些简单的逻辑处理,这里就不再贴代码了。最后我就编辑中选择图片的功能做一个简单的介绍,这个类似于一个简单的文件浏览器。我们先看看界面

其实实现这个功能是很简单的。先获取设备的目录地址,枚举此目录下所有的文件,然后显示在界面上,如果是图片,就直接将图片的略缩图显示出来。点击图片就直接获取其地址,返回给设置界面,赋值给EditText就可以了。这里我贴出关键代码,作为参考,很有改善余地的。呵呵,各位见谅了…

BrowserFileAdapter.java这个是继承BaseAdapter的,没有什么好说的。

<div style="font-weight: normal; text-align: left; "><span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">package org.winplus.surfing;</span></span></div><span style="font-size:16px;"><span style="font-family: 'Microsoft YaHei'; "></span></span><div style="text-align: left;"><span style="font-weight: normal;"><span style="font-size:16px;">

</span></span></div><span style="font-size:16px;"><span style="font-weight: normal;"></span></span><div style="text-align: left;"><span style="font-size:16px;">import java.util.ArrayList;</span></div><div style="text-align: left;"><span style="font-family: 'Microsoft YaHei'; "><span style="font-weight: normal; "><span style="font-size:16px;">

</span></span></span></div><span style="font-size:16px;"><span style="font-family: 'Microsoft YaHei'; "></span></span><div style="text-align: left;"><span style="font-weight: normal;"><span style="font-size:16px;">

</span></span></div><span style="font-size:16px;"><span style="font-weight: normal;"></span></span><div style="text-align: left;"><span style="font-size:16px;">import android.content.Cont</span></div><div style="text-align: left;"><span style="font-size:16px;">ext;</span></div><div style="text-align: left;"><span style="font-size:16px;">t android.view.LayoutInflater;

impo</span></div><div style="text-align: left;"><span style="font-size:16px;">impo</span></div><div style="text-align: left;"><span style="font-size:16px;">rrt android.view.View;</span></div><div style="text-align: left;"><span style="font-size:16px;">wGroup;

import android.widget.</span></div><div style="text-align: left;"><span style="font-size:16px;">import android.view.Vi</span></div><div style="text-align: left;"><span style="font-size:16px;">eBaseAdapter;</span></div><div style="text-align: left;"><span style="font-size:16px;">import android.widget.ImageView;</span></div><div style="text-align: left;"><span style="font-size:16px;">rowserFileAdapter extends BaseA</span></div><div style="text-align: left;"><span style="font-size:16px;">import android.widget.TextView;</span></div><span style="font-size:16px;">

</span><div style="text-align: left;"><span style="font-size:16px;">ublic class </span></div><span style="font-size:16px;">

p

</span><div style="text-align: left;"><span style="font-size:16px;">Bdapter {</span></div><span style="font-size:16px;">

</span><div style="text-align: left;"><span style="font-size:16px;">rayList<DirFileInfo> mDirFileInfos = new ArrayList<DirFileInfo>();

private L</span></div><span style="font-size:16px;">  private A

rayoutInflater inflater;

</span><div style="text-align: left;"><span style="font-size:16px;">(ArrayList<DirFileInfo> dirFileInfos,Context context) {

mDirFileInfos = dirFile</span></div><span style="font-size:16px;">    public BrowserFileAdapte

rInfos;

inflater = LayoutInflater.from(context);

}

@Override

public int getCount() {

</span><div style="text-align: left;"><span style="font-size:16px;">rride

public Object getItem(int </span></div><span style="font-size:16px;">     if (!mDirFileInfos.isEmpty()) {

return mDirFileInfos.size();

}

return 0;

}

@Ov

eposition) {

if (!mDirFileInfos.isEmpty()) {

return mDirFileInfos.get(position);

}

return null;

}

@Override

</span><div style="text-align: left;"><span style="font-size:16px;">iewGroup parent) {

ViewHolder  holde</span></div><span style="font-size:16px;">  public long getItemId(int position) {

return 0;

}

@Override

public View getView(int position, View convertView,

Vr  = null;

DirFileInfo dirFileInfo = mDirFileInfos.get(position);

if (convertView == null) {

holder = new ViewHolder();

</span><div style="text-align: left;"><span style="font-size:16px;">.list_photo_icon);

holder.name = (TextView) convertView.findViewBy</span></div><span style="font-size:16px;">            convertView = inflater.inflate(R.layout.list_photo_browser, null);

holder.icon = (ImageView) convertView.findViewById(R.i

dId(R.id.list_photo_name);

convertView.setTag(holder);

}else {

holder = (ViewHolder) convertView.getTag();

}

holder.icon.setBackgroundDrawable(dirFileInfo.getIcon());

</span><div style="text-align: left;"></div><span style="font-size:16px;">      holder.name.setText(dirFileInfo.getName());

return convertView;

}

private class ViewHolder{

public ImageView icon;

public TextView name;

}

</span><div style="text-align: left;"></div><span style="font-size:16px;">}

</span>

DirFileInfo.java(目录文件信息)

<span style="font-weight: normal;"><span style="font-family: 'Microsoft YaHei'; "><span style="font-size:16px;">public class DirFileInfo{

private Drawable icon;

private String name;

private String path;

private boolean isDir;

// getter。Setter

}</span></span></span>

BrowserPhotoActivity.java(Activity显示界面)

<span style="font-size:16px;">package org.winplus.surfing;

import java.util.ArrayList;

import android.app.Activity;

import android.content.Context;

import android.content.Intent;

import android.os.Bundle;

import android.os.Environment;

import android.util.Log;

import android.view.Gravity;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.ViewGroup.LayoutParams;

import android.widget.AdapterView;

import android.widget.Button;

import android.widget.EditText;

import android.widget.GridView;

import android.widget.ImageView;

import android.widget.LinearLayout;

import android.widget.ListView;

import android.widget.AdapterView.OnItemClickListener;

public class BrowserPhotoActivity extends Activity implements OnClickListener,

OnItemClickListener {

private static final String TAG = "BrowserPhotoActivity";

private EditText edtPath;

//  private Button btnList;

//  private Button btnGrid;

private ListView lstFile;

private GridView gidFile;

private Context mContext;

private BrowserFileAdapter fileAdapter;

private ArrayList<DirFileInfo> dirFileInfos = new ArrayList<DirFileInfo>();

private static StringBuffer mPath = new StringBuffer();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.photo_browser);

setupViews();

}

private void setupViews() {

edtPath = (EditText) findViewById(R.id.photo_path);

//btnGrid = (Button) findViewById(R.id.photo_grid_display);

//btnList = (Button) findViewById(R.id.photo_list_display);

lstFile = (ListView) findViewById(R.id.photo_list);

gidFile = (GridView) findViewById(R.id.photo_grid);

gidFile.setVisibility(View.GONE);

//btnGrid.setOnClickListener(this);

//btnList.setOnClickListener(this);

lstFile.setOnItemClickListener(this);

mContext = BrowserPhotoActivity.this;

// 添加表头

LinearLayout layout=new LinearLayout(mContext);

layout.setGravity(Gravity.LEFT);

ImageView imageView = new ImageView(mContext);

imageView.setBackgroundResource(R.drawable.ic_prev_dir);

layout.addView(imageView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

lstFile.addHeaderView(layout);

mPath.append(Environment.getExternalStorageDirectory().getPath());

getCurrentPathFileDir(mPath.toString());

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.photo_grid:

break;

case R.id.photo_list:

break;

default:

break;

}

}

@Override

public void onItemClick(AdapterView<?> parent, View view, int position,

long id) {

if (position==0) {

int pathCount = mPath.toString().lastIndexOf("/");

if (pathCount==0) {

mPath.delete(0, mPath.length()).append("/");

}else {

mPath.delete(pathCount,mPath.length());

}

getCurrentPathFileDir(mPath.toString());

}else {

DirFileInfo dirFileInfo = (DirFileInfo) lstFile.getItemAtPosition(position);// 得到当前点击的对象

if(dirFileInfo.isDir()){

// 为目录时,点击显示下级目录中的图片文件和文件夹

Log.i(TAG, "is dir");

Log.i(TAG, "mpath="+mPath+";dirFileInfo.name="+dirFileInfo.getName());

mPath.append("/").append(dirFileInfo.getName());

getCurrentPathFileDir(mPath.toString());

}else {

// 选择图片

Intent intent = new Intent();

Log.i(TAG, "dirFileInfo.getPath()="+dirFileInfo.getPath());

intent.putExtra("iconAbsPath",dirFileInfo.getPath());

setResult(Constant.BROWSER_IMAGE, intent);

finish();

Log.i(TAG, "is file");

}

}

}

/**

* 得到当前目录下的图片文件和文件夹

* @param path

*/

private void getCurrentPathFileDir(String path){

edtPath.setText(path);

dirFileInfos = Utils.getDirFile(path, mContext);

fileAdapter = new BrowserFileAdapter(dirFileInfos, mContext);

lstFile.setAdapter(fileAdapter);

fileAdapter.notifyDataSetChanged();

}

}

</span>

五、扩展,我们在界面设计中看到有一个排序功能,这是一个扩展的功能,其实在这个应用程序中可以不做,意义不大,如果非要做这个功能的话,我建议做成Music中能够通过拖动列表进行排序这样的功能

转载于:https://www.cnblogs.com/android-blogs/p/4891076.html

Android项目实战之(1)-- 开发一个快速冲浪的程序相关推荐

  1. Android项目实战--手机卫士

    Android项目实战--手机卫士--结束 很久都没有来更新博客了,之前一直忙着工作的事,接触到了一些以前从来没有接触过的东西,真的挺有挑战性的,但也有很多的无奈,但也学习到了很多东西,我会慢慢的写到 ...

  2. 开发android项目实战,Android 项目实战:手机安全卫士开发案例解析

    Android 项目实战:手机安全卫士开发案例解析 作 者:王家林,王家俊,王家虎 出版时间:2013 丛编项:移动互联应用开发系列 内容简介 本书通过对一款手机安全卫士开发案例的详细解析,讲解了一个 ...

  3. Android项目实战(二十二):启动另一个APP or 重启本APP

    Android项目实战(二十二):启动另一个APP or 重启本APP 原文:Android项目实战(二十二):启动另一个APP or 重启本APP 一.启动另一个APP 目前公司项目需求,一个主AP ...

  4. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

  5. (转载)Android项目实战(二十八):使用Zxing实现二维码及优化实例

    Android项目实战(二十八):使用Zxing实现二维码及优化实例 作者:听着music睡 字体:[增加 减小] 类型:转载 时间:2016-11-21 我要评论 这篇文章主要介绍了Android项 ...

  6. Android项目实战系列—基于博学谷(七)课程模块(上)

    由于这个模块内容较多,分为上.中.下 三篇博客分别来讲述,请耐心阅读. 课程模块分为四个部分 课程列表 课程详情 视频播放 播放记录 课程模块(上)主要讲述课程列表部分 一.水平滑动广告栏界面 1.创 ...

  7. 学习笔记之《Android项目实战——手机安全卫士》

    [Android项目实战-手机安全卫士] 目标:快速积累开发经验,具备中级Android工程师能力. 如遇到难以理解的逻辑或功能,可以先将程序打断点观察程序的执行逻辑. 第一章项目简介:欢迎界面.主界 ...

  8. Android项目实战(三十二):圆角对话框Dialog

    原文:Android项目实战(三十二):圆角对话框Dialog 前言: 项目中多处用到对话框,用系统对话框太难看,就自己写一个自定义对话框. 对话框包括:1.圆角 2.app图标 , 提示文本,关闭对 ...

  9. 《Android项目实战-博学谷》应用图标欢迎界面

    前言 本项目使用Android Studio 3.0.1作为开发工具,参照传智播客教材<Android项目实战--博学谷> 创建项目 可参照落萚简书文集--Android安全卫士开发笔记, ...

  10. 关于《基于eclipse的android项目实战—博学谷》的问题,为了这个差点疯了

    前面都是废话,想要干的直接点我你就对了 <基于eclipse的android项目实战-博学谷>这篇文章已经一个星期没有更新了,原因是后面出了些问题,然后我花了整整一个星期才解决. < ...

最新文章

  1. u盘复制不进去东西_确认过眼神,是电脑小白的福音!U启动U盘启动盘制作工具...
  2. C语言丨线性表(二):线性链表(单链表)
  3. php7.1 伪静态失效,解决CodeIgniter伪静态失效
  4. 专门跑顺风车真的挣钱吗?
  5. python定义字典对象时_Python对象类型之字典
  6. CIS-Linux Centos7最新基线标准进行系统层面基线检测
  7. lighttpd出现mod_indexfile.so: cannot open shared object file: No such file or directory
  8. MongoDB分片存储集群支撑海量数据
  9. java导出功能(多个sheet页数据导出)
  10. 刷题day_12:快乐数
  11. GSM劫持+短信嗅探是什么,如何防范指南
  12. Android案例手册 - 定位点圆形水波纹和椭圆水波纹
  13. 记录JVM中Eden区、Survivor from区和Survivor to区及Minor GC和Major GC的理解
  14. Star CCM+如何修改默认单位
  15. 跨时钟域(CDC)优秀文章汇总-持续收集
  16. IntelliJ IDEA 搭建一个比较完整的网站实例 1
  17. 决定高薪的细节守则 2012_07_28
  18. 艾美捷曲妥珠单抗Trastuzumab参数和相关研究
  19. 论文阅读,GAN 生成对抗网络 2014 Goodfellow原文阅读笔记
  20. 《推理的迷宫》读书笔记之三

热门文章

  1. Idea2020版本设置编码格式
  2. scala 偏函数及其用途
  3. Cmake构建_选择debug与release的库
  4. CRT、ATL、MFC 三者介绍和关系
  5. [NOI2017]整数
  6. [CodeForces - 276A] Lunch Rush
  7. 农广传媒获中体基金2000万元Pre-A轮融资
  8. 1.13 单用户模式;1.14 救援模式;1.15 克隆虚拟机;1.16 Linux机器相互登录
  9. content 控件(24)
  10. pandas.DataFrame.rank