Android項目實戰之(1)-- 開發一個"快速衝浪"的程序

概述:這個小程序,你講學習到基本控件(Button,Listview,Gridview,TextView等)的使用技巧,AssetManager類的使用,XML數據的解析方式,BaseAdapter,幾種佈局的使用,Sqlite的使用等等。java

 

 1、簡單需求設計android

    左右分欄的形式,左欄爲一級菜單,右欄爲二級菜單。經過點擊一級菜單列表在右欄現實二級菜單,在二級菜單中點擊相關選項進入網頁。一級菜單,二級菜單均有編輯功能,或增,或刪,或改。爲提升用戶體驗,將二級菜單的顯示模式定爲兩種,一爲列表模式,二爲表格方式。將在一二級菜單中添加列表頭或者列表尾進行添加操做。爲更迎合用戶的偏好使用,將設計將用戶本次的設置保存到系統中,方便用戶下次使用。提供恢復默認設置的選項,即將數據初始爲原始狀態。sql

 

2、技術實現可能性預見數據庫

    就我本人而言,作過的項目很少,那麼首先我會作一個簡單的技術考察,或作一些小Demo。那麼這裏,我就以個人方式來闡述下這個項目須要用到的一個技術。首先,必然要用到xml的解析,那麼xml有三種解析方式(這裏只作簡單說明):(1)DOM解析,Dom解析器會在解析xml文檔是,把文檔中全部的元素按照其出現的層次關係解析成一個個Node節點對象,Node節點對象提供了相應的方法去獲取父節點和子節點,那麼咱們能夠經過這些方法讀取整個xml並對其操做,它的缺點是一次性加載整個xml文件,那麼對於移動設備,內存是很是寶貴的,因此咱們不用它。(2)SAX解析,SAX解析容許在讀取文檔的時候,即對文檔進行處理,而沒必要等到整個文檔撞在完畢纔對其進行操做。它是基於事件驅動的(3)PULL方式,和SAX同樣也是基於事件驅動的,使用起來也比較簡單,那麼咱們就用Pull。Activity之間的數據傳遞和保存,這個是徹底能夠處理的。調用相應的方法,或者將須要存儲的數據保存到sqlite就能夠了。這些開始開代碼吧。功能的實現,基本沒有什麼問題了,那麼下一步咱們就來佈局下界面。小程序

 

3、界面佈局的設計數組

    根據以前的需求設計,大體將界面佈局定爲如下樣式。瀏覽器

 

 

 

4、項目關鍵技術點講解及代碼展現緩存

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

 

二、按照個人編碼習慣,我喜歡先將UI弄好,而後直接取數據,並將其顯示。那麼根據上圖UI的設計草圖,咱們能夠將幾個主要的佈局進行排版設計。代碼以下:ide

 

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;"> 

</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> 

 

5、擴展,咱們在界面設計中看到有一個排序功能,這是一個擴展的功能,其實在這個應用程序中能夠不作,意義不大,若是非要作這個功能的話,我建議作成Music中可以經過拖動列表進行排序這樣的功能

相關文章
相關標籤/搜索