Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實戰:手把手實現“捧腹網”APP(三)-----UI實現,邏輯實現

Android實戰:手把手實現“捧腹網”APP(三)-----UI實現,邏輯實現

編輯:關於Android編程

APP頁面實現

這裡寫圖片描述
根據原型圖,我們可以看出,UI分為兩部分,底部Tab導航+上方列表顯示。 所以此處,我們通過 FragmentTabHost+Fragment,來實現底部的導航頁面,通過RecyclerView來實現列表頁面。
因為篇幅原因,關於FragmentTabHost和RecyclerView的使用,不多做介紹,可以建議參考: FragmentTabHost使用方法及RecycleView_PullToRefresh_LoadMore兩篇文章,其中後者關於Recyclerview的項目是我之前封裝的一個支持下拉刷新,加載更多,添加Header和Footer等功能的RecyclerView,便於使用。

此處,再多說一點,因為是我們做自己來實現該ui,沒美工給我設計圖,切圖標, 所以我們需要自己去找圖標,此處推薦Iconfont-阿裡巴巴矢量圖標庫, 在這裡,我們可以找到很多的圖標,選擇適用的幾個即可。

篇幅原因,具體的頁面布局、實現代碼,我這裡就不多貼,有興趣的,可以直接看源碼,此處,只貼出列表list_item的頁面布局代碼。
從原型圖中,我們可以看出,列表的顯示分為純文顯示和圖片顯示,所以我們的item布局,應該要兼容這兩種顯示方式。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">

效果圖:
這裡寫圖片描述

我們只需要在實現的邏輯上,控制文字、圖片的顯隱就好了。

APP邏輯功能實現

1.數據獲取,實現列表適配器

在第一章捧腹網網頁分析、數據獲取中,我已經講過了如何去解析網頁中的數據為我們所用,拿到數據後,我們需要用這些數據填充RecyclerView,此處,使用的是我已經封裝好的RecyclerView,支持翻頁加載數據。

import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.lnyp.joke.R;
import com.lnyp.joke.pengfu.JokeBean;
import com.lnyp.joke.widget.CircleImageView;
import com.lnyp.joke.widget.ShowMaxImageView;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 *笑話列表
 */
public class JokeListAdapter extends RecyclerView.Adapter {

    private LayoutInflater mInflater;

    private Fragment mContext;

    private List mDatas;

    private View.OnClickListener onItemClick;

    private int screenWidth;

    public JokeListAdapter(Fragment context, List datas, View.OnClickListener onItemClick) {

        this.mContext = context;

        this.mDatas = datas;

        this.onItemClick = onItemClick;

        mInflater = LayoutInflater.from(context.getActivity());

        DisplayMetrics metric = new DisplayMetrics();
        context.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metric);
        screenWidth = metric.widthPixels;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = mInflater.inflate(R.layout.list_item_joke, parent, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        ViewHolder viewHolder = (ViewHolder) holder;

        JokeBean jokeBean = mDatas.get(position);

        if (jokeBean != null) {

            Glide.with(mContext)
                    .load(jokeBean.getUserAvatar())
                    .asBitmap()
                    .centerCrop()
                    .into(viewHolder.imgUser);

            viewHolder.textUserName.setText(jokeBean.getUserName());
            viewHolder.textLastTime.setText(jokeBean.getLastTime());
            viewHolder.textTitle.setText(jokeBean.getTitle());

            JokeBean.DataBean dataBean = jokeBean.getDataBean();
            if (dataBean != null) {
                if (TextUtils.isEmpty(dataBean.getContent())) {

                    viewHolder.textContent.setVisibility(View.GONE);
                    viewHolder.imgJoke.setVisibility(View.VISIBLE);
                    viewHolder.textTitle.setVisibility(View.VISIBLE);

//                    System.out.println(dataBean.getShowImg() + "    " + dataBean.getGifsrcImg());

                    double width = Double.parseDouble(dataBean.getWidth());
                    double height = Double.parseDouble(dataBean.getHeight());
                    ViewGroup.LayoutParams lp = viewHolder.imgJoke.getLayoutParams();
                    lp.width = (int) (screenWidth * 0.8);
                    lp.height = (int) (screenWidth * 0.8 * height / width);
                    viewHolder.imgJoke.setLayoutParams(lp);

                    String url = dataBean.getShowImg();
                    String gifUrl = dataBean.getGifsrcImg();
                    System.out.println("url : " + url + "  gifUrl : " + gifUrl);
                    if (TextUtils.isEmpty(gifUrl)) {
                        Glide.with(mContext).load(url).asBitmap().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(viewHolder.imgJoke);
                    } else {
                        Glide.with(mContext).load(gifUrl).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(viewHolder.imgJoke);
                    }


                } else {
                    viewHolder.textContent.setVisibility(View.VISIBLE);
                    viewHolder.imgJoke.setVisibility(View.GONE);
                    viewHolder.textTitle.setVisibility(View.GONE);

                    viewHolder.textContent.setText(dataBean.getContent());
                }
            }

            List tags = jokeBean.getTags();
            if (tags != null) {

                int size = tags.size();
                if (size == 0) {
                    updateTags(viewHolder, View.GONE, View.GONE, View.GONE, View.GONE);
                } else if (size == 1) {
                    viewHolder.textTag1.setText(tags.get(0));
                    updateTags(viewHolder, View.VISIBLE, View.GONE, View.GONE, View.GONE);
                } else if (size == 2) {
                    viewHolder.textTag1.setText(tags.get(0));
                    viewHolder.textTag2.setText(tags.get(1));
                    updateTags(viewHolder, View.VISIBLE, View.VISIBLE, View.GONE, View.GONE);
                } else if (size == 3) {
                    viewHolder.textTag1.setText(tags.get(0));
                    viewHolder.textTag2.setText(tags.get(1));
                    viewHolder.textTag3.setText(tags.get(2));
                    updateTags(viewHolder, View.VISIBLE, View.VISIBLE, View.VISIBLE, View.GONE);
                } else {
                    viewHolder.textTag1.setText(tags.get(0));
                    viewHolder.textTag2.setText(tags.get(1));
                    viewHolder.textTag3.setText(tags.get(2));
                    viewHolder.textTag4.setText(tags.get(3));
                    updateTags(viewHolder, View.VISIBLE, View.VISIBLE, View.VISIBLE, View.VISIBLE);
                }
                viewHolder.layoutTags.setVisibility(View.VISIBLE);
            } else {
                updateTags(viewHolder, View.GONE, View.GONE, View.GONE, View.GONE);
                viewHolder.layoutTags.setVisibility(View.GONE);
            }

            viewHolder.imgJoke.setTag(R.string.app_name, position);
            viewHolder.imgJoke.setOnClickListener(onItemClick);
        }
    }

    private void updateTags(ViewHolder viewHolder, int v1, int v2, int v3, int v4) {
        viewHolder.textTag1.setVisibility(v1);
        viewHolder.textTag2.setVisibility(v2);
        viewHolder.textTag3.setVisibility(v3);
        viewHolder.textTag4.setVisibility(v4);
    }

    @Override
    public int getItemCount() {

        return mDatas != null ? mDatas.size() : 0;
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.imgJoke)
        public ShowMaxImageView imgJoke;

        @BindView(R.id.textContent)
        public TextView textContent;

        @BindView(R.id.layoutTags)
        public LinearLayout layoutTags;

        @BindView(R.id.textTitle)
        public TextView textTitle;

        @BindView(R.id.textTag1)
        public TextView textTag1;

        @BindView(R.id.textTag2)
        public TextView textTag2;

        @BindView(R.id.textTag3)
        public TextView textTag3;

        @BindView(R.id.textTag4)
        public TextView textTag4;

        @BindView(R.id.imgUser)
        public CircleImageView imgUser;

        @BindView(R.id.textUserName)
        public TextView textUserName;

        @BindView(R.id.textLastTime)
        public TextView textLastTime;


        public ViewHolder(View itemView) {
            super(itemView);

            ButterKnife.bind(this, itemView);
        }
    }
}

對於RecyclerView的適配器RecyclerView.Adapter的使用方式,相信玩過它的人都很熟悉,裡面的方法不多介紹,主要講下圖片處理這塊的實現:

String url = dataBean.getShowImg();
                    String gifUrl = dataBean.getGifsrcImg();
                    System.out.println("url : " + url + "  gifUrl : " + gifUrl);
                    if (TextUtils.isEmpty(gifUrl)) {
                        Glide.with(mContext).load(url).asBitmap().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(viewHolder.imgJoke);
                    } else {
                        Glide.with(mContext).load(gifUrl).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(viewHolder.imgJoke);
                    }

app中要展示的圖片,分為靜態圖片和動態圖片,glide可以很好的處理gif動態圖的加載,但是,如果像下面這樣直接使用glide加載動態圖,效率可是比較慢的。

Glide.with(mContext).load(gifUrl)into(viewHolder.imgJoke);

所以,在加載gif動態圖的時候,我們通常使用下面這樣的緩存策略

Glide.with(mContext).load(gifUrl).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(viewHolder.imgJoke);

除了圖片加載,還有一點需要講解下,就是下面這段代碼:

double width = Double.parseDouble(dataBean.getWidth());
                    double height = Double.parseDouble(dataBean.getHeight());
                    ViewGroup.LayoutParams lp = viewHolder.imgJoke.getLayoutParams();
                    lp.width = (int) (screenWidth * 0.8);
                    lp.height = (int) (screenWidth * 0.8 * height / width);
                    viewHolder.imgJoke.setLayoutParams(lp);

這段代碼的意思是,在加載圖片之前,先設置了ImageView的寬高。這樣做的目的,是為了在圖片加載顯示之前就固定ImageView的大小,避免了列表因為圖片高度不一致而出現“晃動”。

好,到這裡,我們基本完成了列表顯示功能了,接下來,在Fragment中實現功能邏輯。

2.實現列表邏輯功能

package com.lnyp.joke.fragment;

import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.baoyz.widget.PullRefreshLayout;
import com.lnyp.flexibledivider.HorizontalDividerItemDecoration;
import com.lnyp.joke.R;
import com.lnyp.joke.adapter.JokeListAdapter;
import com.lnyp.joke.http.HttpUtils;
import com.lnyp.joke.pengfu.JokeApi;
import com.lnyp.joke.pengfu.JokeBean;
import com.lnyp.joke.pengfu.JokeUtil;
import com.lnyp.joke.widget.SmartisanDrawable;
import com.lnyp.recyclerview.EndlessRecyclerOnScrollListener;
import com.lnyp.recyclerview.HeaderAndFooterRecyclerViewAdapter;
import com.lnyp.recyclerview.RecyclerViewLoadingFooter;
import com.lnyp.recyclerview.RecyclerViewStateUtils;
import com.victor.loading.rotate.RotateLoading;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;

public class MainFragment extends Fragment {

    private Unbinder unbinder;

    @BindView(R.id.rotateloading)
    public RotateLoading rotateloading;

    @BindView(R.id.swipeRefreshLayout)
    public PullRefreshLayout swipeRefreshLayout;

    @BindView(R.id.listInspirations)
    public RecyclerView listInspirations;

    private HeaderAndFooterRecyclerViewAdapter mAdapter;

    private List mDatas;

    private int page = 1;

    private boolean mHasMore = false;

    private boolean isRefresh = true;


    // 處理請求返回信息
    private MyHandler mHandler = new MyHandler();

    private class MyHandler extends Handler {

        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case 0:

                    RecyclerViewStateUtils.setFooterViewState(listInspirations, RecyclerViewLoadingFooter.State.Normal);
                    swipeRefreshLayout.setRefreshing(false);
                    rotateloading.stop();

                    mAdapter.notifyDataSetChanged();

                    break;
            }
        }
    }

    public MainFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_main, container, false);

        unbinder = ButterKnife.bind(this, view);

        initView();

        rotateloading.start();
        refreshReq();

        return view;
    }

    private void initView() {

        mDatas = new ArrayList<>();

        JokeListAdapter jokeListAdapter = new JokeListAdapter(this, mDatas, onClickListener);
        mAdapter = new HeaderAndFooterRecyclerViewAdapter(jokeListAdapter);
        listInspirations.setAdapter(mAdapter);

        listInspirations.setLayoutManager(new LinearLayoutManager(getActivity()));
        listInspirations.addItemDecoration(
                new HorizontalDividerItemDecoration.Builder(getActivity())
                        .colorResId(R.color.divider_color)
                        .build());

        listInspirations.addOnScrollListener(mOnScrollListener);

        swipeRefreshLayout.setOnRefreshListener(onRefreshListener);

        swipeRefreshLayout.setRefreshDrawable(new SmartisanDrawable(getActivity(), swipeRefreshLayout));
        swipeRefreshLayout.setBackgroundColor(Color.parseColor("#EFEFEF"));
        swipeRefreshLayout.setColor(Color.parseColor("#8F8F81"));

    }

    private PullRefreshLayout.OnRefreshListener onRefreshListener = new PullRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            refreshReq();
        }
    };

    private void refreshReq() {

        isRefresh = true;

        page = 1;

        qryJokes();
    }

    private void qryJokes() {

        final String url = JokeApi.PENGFU_NEW_JOKES + page + JokeApi.URL_SUFFIX;
        System.out.println(url);

        HttpUtils.doGetAsyn(url, new HttpUtils.CallBack() {

            @Override
            public void onRequestComplete(String result) {

                if (result == null) {
                    return;
                }
                System.out.println(result);

                Document doc = Jsoup.parse(result);

                if (doc != null) {

                    JokeUtil jokeUtil = new JokeUtil();
                    List jokeBeens = jokeUtil.getNewJokelist(doc);

                    if (jokeBeens != null) {

                        page++;
                        mHasMore = true;

                        if (isRefresh) {
                            mDatas.clear();
                            isRefresh = false;
                        }

                        mDatas.addAll(jokeBeens);

                        mHandler.sendEmptyMessage(0);

                    }
                }
            }
        });

    }

    private EndlessRecyclerOnScrollListener mOnScrollListener = new EndlessRecyclerOnScrollListener() {
        @Override
        public void onLoadNextPage(View view) {
            super.onLoadNextPage(view);

            RecyclerViewLoadingFooter.State state = RecyclerViewStateUtils.getFooterViewState(listInspirations);

            if (state == RecyclerViewLoadingFooter.State.Loading) {
                return;
            }

            if (mHasMore) {
                RecyclerViewStateUtils.setFooterViewState(getActivity(), listInspirations, mHasMore, RecyclerViewLoadingFooter.State.Loading, null);
                qryJokes();

            } else {
                RecyclerViewStateUtils.setFooterViewState(getActivity(), listInspirations, mHasMore, RecyclerViewLoadingFooter.State.TheEnd, null);
            }
        }
    };

    private View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            try {
                int pos = (int) view.getTag(R.string.app_name);
                JokeBean jokeBean = mDatas.get(pos);
                String showImg = jokeBean.getDataBean().getShowImg();
                String gifSrcImg = jokeBean.getDataBean().getGifsrcImg();
//
                System.out.println(showImg + "   " + gifSrcImg);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    };

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }
}

功能邏輯比較簡單,不多做解釋。 到這裡,我們的“捧腹”APP已經完成了80%了。下面,在做些擴展性的功能,使得它更像一個完整的APP。

3. 圖片的大圖浏覽功能

上篇博文,我們就提到了,我們要使用PhotoView實現大圖的浏覽功能。
PhotoView的使用,可以直接在它的github官方介紹上看到,下面,我直接貼出使用代碼。

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;

import butterknife.BindView;
import butterknife.ButterKnife;
import uk.co.senab.photoview.PhotoViewAttacher;

/**
 * 圖片浏覽
 */
public class PhotoActivity extends FragmentActivity {

    @BindView(R.id.imgJoke)
    public ImageView imgJoke;

    private String showImg;
    private String gifSrcImg;

    private PhotoViewAttacher mAttacher;

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

        ButterKnife.bind(this);

        mAttacher = new PhotoViewAttacher(imgJoke);

        showImg = getIntent().getStringExtra("showImg");
        gifSrcImg = getIntent().getStringExtra("gifSrcImg");
        System.out.println(showImg + "   " + gifSrcImg);

        if (TextUtils.isEmpty(gifSrcImg)) {
            Glide.with(this).load(showImg).asBitmap().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imgJoke);
        } else {
            Glide.with(this).load(gifSrcImg).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imgJoke);
        }

        mAttacher.update();

        mAttacher.setOnPhotoTapListener(new PhotoViewAttacher.OnPhotoTapListener() {
            @Override
            public void onPhotoTap(View view, float x, float y) {
                PhotoActivity.this.finish();
            }

            @Override
            public void onOutsidePhotoTap() {
                PhotoActivity.this.finish();
            }
        });
    }
}

程序中,我們添加了一個事件監聽,主要是為了在用戶單擊圖片顯示或者不顯示部分時,可以退出浏覽。

4.為APP加入Bughd 實現崩潰分析、版本更新功能

功能做到這裡,基本上完成了90%的APP,接下來,我們為其加入崩潰分析、版本更新功能。
關於如何配置,大家直接到http://bughd.com/doc/android官網看,看官方文檔,為app添加功能,是開發的基本能力,而且,這個功能集成並不困難,建議大家自己添加,有疑問,可參考我最後放出的源碼。

APP打包發布

截止此處,我們的“捧腹”APP基本上就已經實現了,在說打包發布之前,我要提到一個很重要的問題,那就是數據版權。 我們知道,這個app的數據,是分析“捧腹網”的網頁,拿到的,我們應當尊重其版權所有。 因為我們是學習使用,所以大家應在app明顯的位置,加上數據來源。這裡,我選擇在啟動頁面上添加。
這裡寫圖片描述

接下來,就是打包發布了。關於如何打包app,限於篇幅,請參考我之前寫的 Android Studio(十二):打包多個發布渠道的apk文件 ,打包apk成功後,我們將其發布在fir.im免費托管分發服務的平台上,方便大家下載測試。(如果沒問題,可以上傳到應用市場)。
這裡寫圖片描述
這裡寫圖片描述
最後,讓我們看下APP最終效果。
這裡寫圖片描述

項目小結:
如果你能耐下心來,看完這三篇實戰博文,相信你也可以做一個簡單的app了。這個app實現並不難,博文講解的也算是詳盡,很容易理解。
這系列的博文,主要是針對初中級開發者,幫大家在研發過程中,理清思路,一步步完成一個完整的app。希望看完這篇博文的朋友,也能夠舉一反三, 做出一個自己所屬的app。

源碼地址:https://github.com/zuiwuyuan/Joke

歡迎有問題的朋友,留言討論,也歡迎進QQ群來討論交流:487786925( Android研發村 ),謝謝大家的支持。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved