1. 程式人生 > >Android 實踐:做一款新聞 APP

Android 實踐:做一款新聞 APP

跟程式碼相關的工作,大多唯手熟爾,所以這裡花了點時間做了款簡易版的新聞 APP,雖然都是些基礎的內容,不過還是可以加深自己對部分程式碼的理解。至少,可以加深自己的記憶

步驟

  1. 依賴庫
  2. 網路請求
  3. 網路解析
  4. 介面佈局
  5. 最後
  6. 執行介面
  7. 執行GIF
  8. 完整程式碼下載地址(github)

依賴庫

過程中需要用到一些開源依賴庫檔案,先在 build.grade 中宣告:

    compile 'com.google.code.gson:gson:2.8.0'       //網路解析
    compile 'com.squareup.okhttp3:okhttp:3.7.0'     //網路請求
compile 'com.github.bumptech.glide:glide:3.8.0' //圖片載入 compile 'com.android.support:design:24.2.1' //Material Design中用到的依賴庫 compile 'de.hdodenhof:circleimageview:2.1.0' //顯示圓形圖片

網路請求

在包下建立一個資料夾 util 用來存放工具類,建立檔案 HttpUtil.class 用來請求資料:

public class HttpUtil {
    public static void
sendOkHttpRequest(String address, okhttp3.Callback callback){ OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(address).build(); client.newCall(request).enqueue(callback); } }

這裡用到的是 okhttp3.Callback 的回撥介面,結果會返回到 callback 的回撥函式中,後面會進行處理

網路解析

我們先從資料解析開始,畢竟這才是這個小專案的重點。這次專案使用的資料來源是天行資料(http://www.tianapi.com/ )的新聞資訊 API ,先看 API 的說明:

新聞資訊 API

可以看到返回資料為 JSON, 預設返回 10 條引數。請求地址為:

這裡寫圖片描述

其中, APIKEY 需要用個人的 API KEY 代替,可以在個人中心中看到,其他的請求地址也是大同小異

這裡寫圖片描述

JSON 返回示例:

這裡寫圖片描述

還有錯誤返回碼,用來判斷返回資料的異常情況:

這裡寫圖片描述

根據 gson 的返回示例,我們可以寫出對應的實體類檔案,通過 gson 將返回資料轉化為對應的型別。先建立一個 gson 資料夾存放實體類檔案。

在 gson 資料夾下建立 New.class 檔案:

public class News {
    @SerializedName("ctime")
    public String time;

    public String title;

    public String description;

    public String picUrl;

    public String url;

}

建立 NewsList.class 檔案:

public class NewsList {

    public int code;

    public String msg;

    @SerializedName("newslist")
    public List<News> newsList ;

}

至此,我們就已經建立好了與返回資料對應的實體類。

在 util 資料夾下建立檔案 Utility.class 檔案:

public class Utility {
    public static NewsList parseJsonWithGson(final String requestText){
        Gson gson = new Gson();
        return gson.fromJson(requestText, NewsList.class);
    }

}

將請求得到的資料解析為 NewList 實體類物件。現在網路請求和解析都準備好了,就開始介面檔案了

介面佈局

修改 values 目錄下的 styles.xml 檔案:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimary</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

修改通知欄顏色和標題欄顏色一樣,是處於視覺統一的原因,也可以不修改(非必須)

主要採用的是 Material Design 的設計,整體佈局採用的是滑動選單,主介面內容為 ToolBar 和 ListView(這裡為了方便,就直接使用),側邊欄內容為 NavigationView

主介面:
因為要用 ToolBar 替代 ActionBar, 我們先修改 values 下面的 styles 檔案,修改主題為:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

在layout 下建立 nav_header 檔案

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:background="@color/colorPrimary"
    android:padding="10dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/icon_image"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        android:src="@drawable/nav_icon" />

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="https://github.com/lentitude"
        android:textColor="@color/color_White"
        android:textSize="14sp" />


    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/username"
        android:text="lentitude"
        android:textColor="@color/color_White"
        android:textSize="14sp" />

</RelativeLayout>

這裡在頭部檔案中放置了一個CircleImageView,兩個 TextView,沒有什麼理解難度

在 res 目錄下建立 menu 資料夾,新建 nav_menu.xml 檔案:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_society"
            android:title="社會新聞" />
        <item
            android:id="@+id/nav_county"
            android:title="國內新聞" />
        <item
            android:id="@+id/nav_internation"
            android:title="國際新聞" />
        <item
            android:id="@+id/nav_fun"
            android:title="娛樂新聞" />
        <item
            android:id="@+id/nav_sport"
            android:title="體育新聞" />
        <item
            android:id="@+id/nav_nba"
            android:title="NBA新聞" />
        <item
            android:id="@+id/nav_football"
            android:title="足球新聞" />
        <item
            android:id="@+id/nav_technology"
            android:title="科技新聞" />
        <item
            android:id="@+id/nav_work"
            android:title="創業新聞" />
        <item
            android:id="@+id/nav_apple"
            android:title="蘋果新聞" />
        <item
            android:id="@+id/nav_war"
            android:title="軍事新聞" />
        <item
            android:id="@+id/nav_internet"
            android:title="移動互聯" />
        <item
            android:id="@+id/nav_travel"
            android:title="旅遊諮詢" />
        <item
            android:id="@+id/nav_health"
            android:title="健康知識" />
        <item
            android:id="@+id/nav_strange"
            android:title="奇聞異事" />
        <item
            android:id="@+id/nav_looker"
            android:title="美女圖片" />
        <item
            android:id="@+id/nav_vr"
            android:title="VR科技" />
        <item
            android:id="@+id/nav_it"
            android:title="IT資訊" />


    </group>
</menu>

這裡建立了若干個 ITEM 子項,只有 title,沒有 icon,大家可以自行放置

主介面 activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v7.widget.Toolbar
                android:id="@+id/tool_bar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:contentInsetStart="0dp"
                app:titleTextColor="@color/color_White"
                android:background="@color/colorPrimary"
                />


        </android.support.design.widget.AppBarLayout>


        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <ListView
                android:id="@+id/list_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:divider="@color/color_Background"
                android:dividerHeight="1dp"

                />


        </android.support.v4.widget.SwipeRefreshLayout>

    </android.support.design.widget.CoordinatorLayout>


    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menu"
        />


</android.support.v4.widget.DrawerLayout>

因為是一步到位,所以……大家最好之前用過使用過相同的佈局設計(比如:第一行程式碼)

DrawerLayout 中有兩個直接子佈局檔案:
1. CoordinatorLayout:一種 FrameLayout, 作為顯示主介面內容的最外層佈局
2. NavigationView:作為顯示側邊欄的最外層佈局,不過已經封裝好了,可以直接通過 app:headerLayout 和 app:menu 屬性引用之前我們已經寫好的 頭部和選單佈局檔案

CoordinatorLayout 中有兩個直接子佈局檔案:
1. AppBarLayout :通過 AppBarLayout 屬性,可以將 ToolBar 和 ListView 分隔開,可以對滾動事件進行響應,實現 Material 效果
2. SwipeRefreshLayout:用來重新整理 ListView 中的內容

建立 list_view_item.xml 檔案,設計 ListView 的子項佈局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="@color/color_White">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp">

        <ImageView
            android:id="@+id/title_pic"
            android:layout_width="80dp"
            android:layout_height="60dp"
            android:layout_centerVertical="true"
            android:layout_alignParentRight="true"
            android:scaleType="centerCrop"/>

        <TextView
            android:id="@+id/title_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:layout_marginRight="10dp"
            android:layout_alignTop="@+id/title_pic"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@+id/title_pic"
            />

        <TextView
            android:id="@+id/descr_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="8sp"
            android:layout_marginRight="10dp"
            android:layout_alignBottom="@+id/title_pic"
            android:layout_alignParentLeft="true"
            />

    </RelativeLayout>


</RelativeLayout>

子項佈局內包含 3 個控制元件,ImageView 顯示返回的圖片,TextView 顯示返回的標題和出處

建立一個 Title.class類:

public class Title {
    private String title;
    private String descr;
    private String imageUrl;
    private String uri;

    public Title(String title,String descr, String imageUrl, String uri){
        this.title = title;
        this.imageUrl = imageUrl;
        this.descr = descr;
        this.uri = uri;
    }

    public String getTitle() {
        return title;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public String getDescr() {
        return descr;
    }

    public String getUri() {
        return uri;
    }
}

這裡之所以除了 標題,出處,圖片顯示在 ListViw 中,uri 傳入另一個佈局,顯示內容檔案

接下來就是 TitleAdapter.class

public class TitleAdapter extends ArrayAdapter<Title> {
    private int resourceId;

    public TitleAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Title> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Title title = getItem(position);
        View view;
        ViewHolder viewHolder;
        /**
         * 快取佈局和例項,優化 listView
         */
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            viewHolder = new ViewHolder();
            viewHolder.titleText = (TextView)view.findViewById(R.id.title_text);
            viewHolder.titlePic = (ImageView) view.findViewById(R.id.title_pic);
            viewHolder.titleDescr = (TextView)view.findViewById(R.id.descr_text);
            view.setTag(viewHolder);
        }else{
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }

        Glide.with(getContext()).load(title.getImageUrl()).into(viewHolder.titlePic);
        viewHolder.titleText.setText(title.getTitle());
        viewHolder.titleDescr.setText(title.getDescr());

        return view;

    }

    public class ViewHolder{
        TextView titleText;
        TextView titleDescr;
        ImageView titlePic;
    }
}

這裡還是一樣的老套路,通過convertView 來快取佈局,通過類 ViewHolder 快取控制元件例項,這樣做,可以節省 50% 的效率,所以還是按照老套路走吧。

接下來就是 Activity 檔案 MainActivity.class:

public class MainActivity extends AppCompatActivity {
    private static final int  ITEM_SOCIETY= 1;
    private static final int  ITEM_COUNTY= 2;
    private static final int  ITEM_INTERNATION= 3;
    private static final int  ITEM_FUN= 4;
    private static final int  ITEM_SPORT= 5;
    private static final int  ITEM_NBA= 6;
    private static final int  ITEM_FOOTBALL= 7;
    private static final int  ITEM_TECHNOLOGY= 8;
    private static final int  ITEM_WORK= 9;
    private static final int  ITEM_APPLE= 10;
    private static final int  ITEM_WAR= 11;
    private static final int  ITEM_INTERNET= 12;
    private static final int  ITEM_TREVAL= 13;
    private static final int  ITEM_HEALTH= 14;
    private static final int  ITEM_STRANGE= 15;
    private static final int  ITEM_LOOKER= 16;
    private static final int  ITEM_VR= 17;
    private static final int  ITEM_IT= 18;


    private List<Title> titleList = new ArrayList<Title>();
    private ListView listView;
    private TitleAdapter adapter;
    private NavigationView navigationView;
    private DrawerLayout drawerLayout;
    private SwipeRefreshLayout refreshLayout;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar)findViewById(R.id.tool_bar);
        setSupportActionBar(toolbar);
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
        }
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle("社會新聞");

        refreshLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_layout);
        refreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));
        listView = (ListView)findViewById(R.id.list_view);
        adapter = new TitleAdapter(this,R.layout.list_view_item, titleList);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            Intent intent = new Intent(MainActivity.this, ContentActivity.class);
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Title title = titleList.get(position);
                intent.putExtra("title",actionBar.getTitle());
                intent.putExtra("uri",title.getUri());
                startActivity(intent);
            }
        });

        drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
        navigationView = (NavigationView)findViewById(R.id.nav_view);
        navigationView.setCheckedItem(R.id.nav_society);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){
                    case R.id.nav_society:
                        handleCurrentPage("社會新聞",ITEM_SOCIETY);
                        break;
                    case R.id.nav_county:
                        handleCurrentPage("國內新聞",ITEM_COUNTY);
                        break;
                    case R.id.nav_internation:
                        handleCurrentPage("國際新聞",ITEM_INTERNATION);
                        break;
                    case R.id.nav_fun:
                        handleCurrentPage("娛樂新聞",ITEM_FUN);
                        break;
                    case R.id.nav_sport:
                        handleCurrentPage("體育新聞",ITEM_SPORT);
                        break;
                    case R.id.nav_nba:
                        handleCurrentPage("NBA新聞",ITEM_NBA);
                        break;
                    case R.id.nav_football:
                        handleCurrentPage("足球新聞",ITEM_FOOTBALL);
                        break;
                    case R.id.nav_technology:
                        handleCurrentPage("科技新聞",ITEM_TECHNOLOGY);
                        break;
                    case R.id.nav_work:
                        handleCurrentPage("創業新聞",ITEM_WORK);
                        break;
                    case R.id.nav_apple:
                        handleCurrentPage("蘋果新聞",ITEM_APPLE);
                        break;
                    case R.id.nav_war:
                        handleCurrentPage("軍事新聞",ITEM_WAR);
                        break;
                    case R.id.nav_internet:
                        handleCurrentPage("移動互聯",ITEM_INTERNET);
                        break;
                    case R.id.nav_travel:
                        handleCurrentPage("旅遊資訊",ITEM_TREVAL);
                        break;
                    case R.id.nav_health:
                        handleCurrentPage("健康知識",ITEM_HEALTH);
                        break;
                    case R.id.nav_strange:
                        handleCurrentPage("奇聞異事",ITEM_STRANGE);
                        break;
                    case R.id.nav_looker:
                        handleCurrentPage("美女圖片",ITEM_LOOKER);
                        break;
                    case R.id.nav_vr:
                        handleCurrentPage("VR科技",ITEM_VR);
                        break;
                    case R.id.nav_it:
                        handleCurrentPage("IT資訊",ITEM_IT);
                        break;
                    default:
                        break;
                }
                drawerLayout.closeDrawers();
                return true;
            }
        });

        refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshLayout.setRefreshing(true);
                int itemName = parseString((String)actionBar.getTitle());
                requestNew(itemName);
            }
        });

        requestNew(ITEM_SOCIETY);

    }

    /**
     *  判斷是否是當前頁面,如果不是則 請求處理資料
     */
    private void handleCurrentPage(String text, int item){
        ActionBar actionBar = getSupportActionBar();
        if (!text.equals(actionBar.getTitle().toString())){
            actionBar.setTitle(text);
            requestNew(item);
            refreshLayout.setRefreshing(true);
        }
    }


    /**
     * 請求處理資料
     */
    public void requestNew(int itemName){

        // 根據返回到的 URL 連結進行申請和返回資料
        String address = response(itemName);    // key
        HttpUtil.sendOkHttpRequest(address, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "新聞載入失敗", Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String responseText = response.body().string();
                final NewsList newlist = Utility.parseJsonWithGson(responseText);
                final int code = newlist.code;
                final String msg = newlist.msg;
                if (code == 200){
                    titleList.clear();
                    for (News news:newlist.newsList){
                        Title title = new Title(news.title,news.description,news.picUrl, news.url);
                        titleList.add(title);
                    }

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            adapter.notifyDataSetChanged();
                            listView.setSelection(0);
                            refreshLayout.setRefreshing(false);
                        };
                    });
                }else{
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "資料錯誤返回",Toast.LENGTH_SHORT).show();
                            refreshLayout.setRefreshing(false);
                        }
                    });
                }



            }
        });


    }

    /**
     * 輸入不同的型別選項,返回對應的 URL 連結
     */
    private String response(int itemName){
        String address = "https://api.tianapi.com/social/?key=339a8b166f397f008236e596616a5f54&num=50&rand=1";
        switch(itemName){
            case ITEM_SOCIETY:
                break;
            case ITEM_COUNTY:
                address = address.replaceAll("social","guonei");
                break;
            case ITEM_INTERNATION:
                address = address.replaceAll("social","world");
                break;
            case ITEM_FUN:
                address = address.replaceAll("social","huabian");
                break;
            case ITEM_SPORT:
                address = address.replaceAll("social","tiyu");
                break;
            case ITEM_NBA:
                address = address.replaceAll("social","nba");
                break;
            case ITEM_FOOTBALL:
                address = address.replaceAll("social","football");
                break;
            case ITEM_TECHNOLOGY:
                address = address.replaceAll("social","keji");
                break;
            case ITEM_WORK:
                address = address.replaceAll("social","startup");
                break;
            case ITEM_APPLE:
                address = address.replaceAll("social","apple");
                break;
            case ITEM_WAR:
                address = address.replaceAll("social","military");
                break;
            case ITEM_INTERNET:
                address = address.replaceAll("social","mobile");
                break;
            case ITEM_TREVAL:
                address = address.replaceAll("social","travel");
                break;
            case ITEM_HEALTH:
                address = address.replaceAll("social","health");
                break;
            case ITEM_STRANGE:
                address = address.replaceAll("social","qiwen");
                break;
            case ITEM_LOOKER:
                address = address.replaceAll("social","meinv");
                break;
            case ITEM_VR:
                address = address.replaceAll("social","vr");
                break;
            case ITEM_IT:
                address = address.replaceAll("social","it");
                break;
            default:
        }
        return address;
    }

    /**
     * 通過 actionbar.getTitle() 的引數,返回對應的 ItemName
     */
    private int parseString(String text){
        if (text.equals("社會新聞")){
            return ITEM_SOCIETY;
        }
        if (text.equals("國內新聞")){
            return ITEM_COUNTY;
        }
        if (text.equals("國際新聞")){
            return ITEM_INTERNATION;
        }
        if (text.equals("娛樂新聞")){
            return ITEM_FUN;
        }
        if (text.equals("體育新聞")){
            return ITEM_SPORT;
        }
        if (text.equals("NBA新聞")){
            return ITEM_NBA;
        }
        if (text.equals("足球新聞")){
            return ITEM_FOOTBALL;
        }
        if (text.equals("科技新聞")){
            return ITEM_TECHNOLOGY;
        }
        if (text.equals("創業新聞")){
            return ITEM_WORK;
        }
        if (text.equals("蘋果新聞")){
            return ITEM_APPLE;
        }
        if (text.equals("軍事新聞")){
            return ITEM_WAR;
        }
        if (text.equals("移動互聯")){
            return ITEM_INTERNET;
        }
        if (text.equals("旅遊資訊")){
            return ITEM_TREVAL;
        }
        if (text.equals("健康知識")){
            return ITEM_HEALTH;
        }
        if (text.equals("奇聞異事")){
            return ITEM_STRANGE;
        }
        if (text.equals("美女圖片")){
            return ITEM_LOOKER;
        }
        if (text.equals("VR科技")){
            return ITEM_VR;
        }
        if (text.equals("IT資訊")){
            return ITEM_IT;
        }
        return ITEM_SOCIETY;
    }

    /**
     * 對側邊欄按鈕進行處理,開啟側邊欄
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:
                drawerLayout.openDrawer(GravityCompat.START);
                break;
            default:
        }
        return true;
    }

    /**
     * 對返回鍵進行處理,如果側邊欄開啟則關閉側邊欄,否則關閉 activity
     */
    @Override
    public void onBackPressed() {
        if(drawerLayout.isDrawerOpen(GravityCompat.START)){
            drawerLayout.closeDrawers();
        }else{
            finish();
        }
    }
}

本文的程式碼量雖然很大,只是比較繁瑣,因為需要根據點選的 ITEM 來對不同的 介面地址提出申請,大部分的函式功能都有進行註釋,所以略過了

public void requestNew(int itemName){

        // 根據返回到的 URL 連結進行申請和返回資料
        String address = response(itemName);    // key
        HttpUtil.sendOkHttpRequest(address, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "新聞載入失敗", Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String responseText = response.body().string();
                final NewsList newlist = Utility.parseJsonWithGson(responseText);
                final int code = newlist.code;
                final String msg = newlist.msg;
                if (code == 200){
                    titleList.clear();
                    for (News news:newlist.newsList){
                        Title title = new Title(news.title,news.description,news.picUrl, news.url);
                        titleList.add(title);
                    }

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            adapter.notifyDataSetChanged();
                            listView.setSelection(0);
                            refreshLayout.setRefreshing(false);
                        };
                    });
                }else{
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "資料錯誤返回",Toast.LENGTH_SHORT).show();
                            refreshLayout.setRefreshing(false);
                        }
                    });
                }



            }
        });


    }
  1. 外部呼叫時傳入 itemName 引數,通過 response() 函式得到所需要請求資料的地址
  2. 通過sendOkHttpRequest() 回撥方法,在返回資料成功的 onResponse() 方法中使用 parseJsonWithGson() 方法獲取對應的實體類
  3. 將實體類中的資料新增到 Title對應中,將 Title 物件新增到 titleList 中,最後通過 runOnUiThread() 方法,切換到主執行緒提醒介面卡進行資料更新。

至此,主介面的程式碼邏輯都已經處理好了,還有 ListView 子項佈局的點選事件處理:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            Intent intent = new Intent(MainActivity.this, ContentActivity.class);
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Title title = titleList.get(position);
                intent.putExtra("title",actionBar.getTitle());
                intent.putExtra("uri",title.getUri());
                startActivity(intent);
            }
        });

在點選 ListView 子項佈局時,會傳入 標題欄文字 和 內容 URL

檔案 activity_content.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/color_White">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >

        <android.support.v7.widget.Toolbar
            android:id=
            
           

相關推薦

Android 實踐可用的天氣 APP

可能很多人會問:之前已經寫過一篇博文來介紹怎麼做一款簡單的新聞APP(http://blog.csdn.net/yiwei12/article/details/71249628),為什麼還要專門一篇來介紹怎麼做一款天氣 APP,畢竟網路請求和資料處理都是大同小異

Android 實踐新聞 APP

跟程式碼相關的工作,大多唯手熟爾,所以這裡花了點時間做了款簡易版的新聞 APP,雖然都是些基礎的內容,不過還是可以加深自己對部分程式碼的理解。至少,可以加深自己的記憶 步驟 依賴庫 網路請求 網路解析 介面佈局 最後 執行介面 執行GIF 完整程式

阿里開發者們的第15個感悟優秀大資料引擎,要找準重點解決的業務場景

1月10日,做一款優秀大資料引擎,要找準重點解決的業務場景。這是我們送給開發者的第15個感悟。 沐遠在社群分享了他的博文,《使用spark分析雲HBase的資料》《hive資料匯入雲hbase》,粉絲評論說請收下我的膝蓋。 李偉(沐遠)阿里雲資料庫技術專家專注大資料分散式計算資料庫領域, 研發Spark及

阿裏開發者們的第15個感悟優秀大數據引擎,要找準重點解決的業務場景

數據 base 開發者 alt 優秀 ces log 理解 社區 1月10日,做一款優秀大數據引擎,要找準重點解決的業務場景。這是我們送給開發者的第15個感悟。 沐遠在社區分享了他的博文,《使用spark分析雲HBase的數據》《hive數據導入雲hbase》,粉絲評論說請

Android開發者必備推薦助力開發的開源APP

今天,給大家推薦一款小而精的開源應用,該應用是同事推薦給我的,我使用後感覺不錯遂在這兒分享給大家。 我們都知道,當我們新接觸一個 Android 專案想要快速熟悉程式碼時,最好的方式就是執行起來,從看得見的入手,一點點去除錯、捋程式碼。而事實上,大多數公司沒有那麼多時間讓你花幾天時間慢慢看,基本

Android自定義EditText手把手教你鍵刪除&自定義樣式的SuperEditText

前言 Android開發中,EditText的使用 非常常見 本文將手把手教你做一款 附帶一鍵刪除功能 & 自定義樣式豐富的 SuperEditText控制元件,希望你們會喜歡。 目錄 1. 簡介 一款 附帶一鍵刪除功

[轉]個人開發者Android App需要知道的事情

原文連結:https://www.cnblogs.com/hubcarl/p/4030884.html目前,應用資訊型別App功能核心都包括內容列表展示、內容詳情展示、個人中心、 評論分享、搜尋四大功能。下面就我以我開發的一款程式設計學習的App[程式設計線上]為例, UI

教你可以賺錢的iPhone APP》02

首先介紹一下開發一款蘋果手機的APP需要的工具 1.     需要一臺裝有蘋果系統(Mac系統)的電腦。如下的電腦均可: iMac Mac Pro MacBook Mac mini 2.     軟體Xcode。 安裝方法: 開啟Mac系統裡自帶的Ap

教你可以賺錢的iPhone APP》03

第1部分 故事的基礎要素 - 程式語言的基礎部分 故事的基礎要素一般包括文字,語句,標點符號等。與之類似,我們這一部分介紹程式語言Swift的基礎部分。 1 變數與常量  1.1 概念通俗講解  變數是指可以變化的量。常量是指不可以變化的量。  以人舉例,人的性

教你可以賺錢的iPhone APP》22

第2部分 故事的進階要素 - 程式語言Swift的進階部分 10 方法 10.1 概念通俗講解 方法是一種特殊的函式,在類或者結構體裡的函式叫方法。其實方法的本質就是函式,只不過它是在類裡,結構體裡使用

教你可以賺錢的iPhone APP》25

第2部分 故事的進階要素 - 程式語言Swift的進階部分 13 擴充套件 13.1 概念通俗講解 擴充套件的作用是為已存在的類、結構體、列舉或者協議型別新增新的功能。 例如,String是一個結構體,

教你可以賺錢的iPhone APP》28

第2部分 故事的進階要素 - 程式語言Swift的進階部分 16 委託 16.1 概念通俗講解 委託是一種設計模式,它使類或結構體通過協議能夠將其某些職責委託給另一個類或結構體,讓另一個類或結構體幫它實

教你可以賺錢的iPhone APP》29

第2部分 故事的進階要素 - 程式語言Swift的進階部分 17 訪問控制 17.1 概念通俗講解 Swift提供了5個訪問級別 open, public, internal, fileprivate,

教你可以賺錢的iPhone APP》30

第2部分 故事的進階要素 - 程式語言Swift的進階部分 18 型別轉換 18.1 概念通俗講解 型別轉換就是轉換當前常量,變數,例項的型別。例如Int型別轉換為String 18.2 定義簡潔介紹 下面介紹4個常用的使用場景 1).普通資料轉換 - 用構造方

仿映客的直播App?看我就夠了

一、直播現狀簡介 Linkee.10 1.技術實現層面: 技術相對都比較成熟,裝置也都支援硬編碼。IOS還提供現成的 Video ToolBox框架,可以對攝像頭和流媒體資料結構進行處理,但Video ToolBox框架只相容8.0以上版本,8.0以下就需要用x264的庫

APP,從設計稿到切圖(Android篇)

依舊宣告:這裡寫的依舊只是某一種工作方法,而不是一種規範,你可以參考,但不要照搬,在具體工作中,一定要靈活運用。 彙總貼,整理了之前零散的關於Android的文章……這裡我把Android的開發文件,字型,以及不同設計尺寸的文件圖片上傳了,喜歡的下載;切圖的工具在IOS的帖子裡,喜歡可以去那下載,這裡就不

果斷收藏APP從設計稿到切圖過程全方位揭祕

9月17日凌晨,IOS9正式推送,它使用的字型最終還是變了,我下面寫的內容你們也要酌情更新,因為我寫的實在趕不上它更新的速度了 iOS9使用的西文字型由Helvetica Neue變更為 San Francisco, iOS9中文字型由此前的黑體-簡變更為蘋方黑體

Android-live如何利用第三方SDK直播軟體

SDK服務選擇 目前有很多做直播雲的服務 樂視雲 網易雲信 保利威視 阿里雲 百度雲 可以根據自己的需要做選擇。 直播基本原理 Push推流 也就是直播端,用於採集視訊資訊上傳,處理等。 Pull拉流 使用者實時播放直播的視訊源 服務端

從零開始設計APP之如何原型圖

@Sophia的玲瓏閣 :這個系列的文章把整個設計過程的經驗總結成文,逐點分享,上期是概述+立項,這期聊聊低保真和高保真原型圖的作用、處理工具和檔案要求等。 Low-fi,即低保真原型圖,整個APP設計階段,設計師真正開始上手的環節。待PM製作好PRD文件和邏輯流程圖之後,

Android實戰_note1(MyMirror_小型攝像處理的App)

最近在實戰做明日科技的一個叫《魔鏡》的APP,接觸到不少有趣的技能tip,這裡記錄一下。功能點主要有啟動頁、攝像頭設定、亮度調節、相機焦距調節。介面選擇換鏡框、吹氣起霧、長安碎屏、搖一搖換鏡框、系統幫助等子功能,部落格會陸續更近 本文自覺有些有趣的地方(這裡僅做摘要,詳見文中):