Android实战:手把手实现“捧腹网”APP(三)-----UI实现,逻辑实现

APP页面实现

这里写图片描述
根据原型图,我们可以看出,UI分为两部分,底部Tab导航+上方列表显示。 所以此处,我们通过 FragmentTabHost+Fragment,来实现底部的导航页面,通过RecyclerView来实现列表页面。 
因为篇幅原因,关于FragmentTabHost和RecyclerView的使用,不多做介绍,可以建议参考: FragmentTabHost使用方法及RecycleView_PullToRefresh_LoadMore两篇文章,其中后者关于Recyclerview的项目是我之前封装的一个支持下拉刷新,加载更多,添加Header和Footer等功能的RecyclerView,便于使用。

此处,再多说一点,因为是我们做自己来实现该ui,没美工给我设计图,切图标, 所以我们需要自己去找图标,此处推荐Iconfont-阿里巴巴矢量图标库, 在这里,我们可以找到很多的图标,选择适用的几个即可。

篇幅原因,具体的页面布局、实现代码,我这里就不多贴,有兴趣的,可以直接看源码,此处,只贴出列表list_item的页面布局代码。 
从原型图中,我们可以看出,列表的显示分为纯文显示和图片显示,所以我们的item布局,应该要兼容这两种显示方式。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:card_view="http://schemas.android.com/apk/res-auto"android:id="@+id/card_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="4dp"android:layout_marginRight="4dp"android:foreground="?android:attr/selectableItemBackground"card_view:cardBackgroundColor="#FFFFFF"card_view:cardCornerRadius="8dp"card_view:cardElevation="2dp"card_view:cardUseCompatPadding="true"><LinearLayout
        android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><RelativeLayout
            android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="6dp"android:gravity="center_vertical"><com.lnyp.joke.widget.CircleImageView
                android:id="@+id/imgUser"android:layout_width="50dp"android:layout_height="50dp"android:src="@mipmap/ic_launcher" /><LinearLayout
                android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="14dp"android:layout_toRightOf="@id/imgUser"android:orientation="vertical"><TextView
                    android:id="@+id/textUserName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:singleLine="true"android:text="textUserNametextUserNametextUserNametextUserNametextUserNametextUserNametextUserNametextUserNametextUserName"android:textColor="#333333"android:textSize="14sp" /><TextView
                    android:id="@+id/textLastTime"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="6dp"android:layout_toRightOf="@id/imgUser"android:gravity="right"android:singleLine="true"android:text="textLastTime"android:textColor="#555555"android:textSize="12sp" /></LinearLayout></RelativeLayout><TextView
            android:id="@+id/textContent"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="4dp"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="4dp"android:gravity="left"android:lineSpacingExtra="4dp"android:text="描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述"android:textColor="#333333"android:textSize="15sp"android:visibility="gone" /><com.lnyp.joke.widget.ShowMaxImageView
            android:id="@+id/imgJoke"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="10dp"android:src="@mipmap/ic_launcher" /><TextView
            android:id="@+id/textTitle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="4dp"android:singleLine="true"android:text="title title title tiltle title"android:textColor="#333333"android:textSize="16sp" /><LinearLayout
            android:id="@+id/layoutTags"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="2dp"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="4dp"android:gravity="right"android:orientation="horizontal"><TextView
                android:id="@+id/textTag1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="4dp"android:layout_marginLeft="4dp"android:background="@drawable/house_tag_text_bg"android:gravity="left"android:paddingBottom="2dp"android:paddingLeft="4dp"android:paddingRight="4dp"android:paddingTop="2dp"android:singleLine="true"android:text="标签"android:textColor="#333333"android:textSize="10sp" /><TextView
                android:id="@+id/textTag2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="4dp"android:layout_marginLeft="4dp"android:background="@drawable/house_tag_text_bg"android:gravity="left"android:paddingBottom="2dp"android:paddingLeft="4dp"android:paddingRight="4dp"android:paddingTop="2dp"android:singleLine="true"android:text="标签"android:textColor="#333333"android:textSize="10sp" /><TextView
                android:id="@+id/textTag3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="4dp"android:layout_marginLeft="4dp"android:background="@drawable/house_tag_text_bg"android:gravity="left"android:paddingBottom="2dp"android:paddingLeft="4dp"android:paddingRight="4dp"android:paddingTop="2dp"android:singleLine="true"android:text="标签"android:textColor="#333333"android:textSize="10sp" /><TextView
                android:id="@+id/textTag4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="4dp"android:layout_marginLeft="4dp"android:background="@drawable/house_tag_text_bg"android:gravity="left"android:paddingBottom="2dp"android:paddingLeft="4dp"android:paddingRight="4dp"android:paddingTop="2dp"android:singleLine="true"android:text="标签"android:textColor="#333333"android:textSize="10sp" /></LinearLayout></LinearLayout></android.support.v7.widget.CardView>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190

效果图: 
这里写图片描述

我们只需要在实现的逻辑上,控制文字、图片的显隐就好了。

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<RecyclerView.ViewHolder> {private LayoutInflater mInflater;private Fragment mContext;private List<JokeBean> mDatas;private View.OnClickListener onItemClick;private int screenWidth;public JokeListAdapter(Fragment context, List<JokeBean> 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;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = mInflater.inflate(R.layout.list_item_joke, parent, false);return new ViewHolder(view);}@Overridepublic 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<String> 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);}@Overridepublic 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);}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207

对于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);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

app中要展示的图片,分为静态图片和动态图片,glide可以很好的处理gif动态图的加载,但是,如果像下面这样直接使用glide加载动态图,效率可是比较慢的。

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

所以,在加载gif动态图的时候,我们通常使用下面这样的缓存策略

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

除了图片加载,还有一点需要讲解下,就是下面这段代码:

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这段代码的意思是,在加载图片之前,先设置了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<JokeBean> 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() {}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentView 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() {@Overridepublic 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() {@Overridepublic 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<JokeBean> 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() {@Overridepublic 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() {@Overridepublic 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();}}};@Overridepublic void onDestroyView() {super.onDestroyView();unbinder.unbind();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228

功能逻辑比较简单,不多做解释。 到这里,我们的“捧腹”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;@Overrideprotected 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() {@Overridepublic void onPhotoTap(View view, float x, float y) {PhotoActivity.this.finish();}@Overridepublic void onOutsidePhotoTap() {PhotoActivity.this.finish();}});}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

程序中,我们添加了一个事件监听,主要是为了在用户单击图片显示或者不显示部分时,可以退出浏览。

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 
apk下载地址: http://fir.im/xiaohane

欢迎有问题的朋友,留言讨论,也欢迎进QQ群来讨论交流:487786925( Android研发村 ),谢谢大家的支持。

版权声明:本文为博主原创文章,未经博主允许不得转载。 http://blog.csdn.net/zuiwuyuan/article/details/52554939

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【前端就业课 第一阶段】HTML5 零基础到实战(八)表单详解

注意&#xff1a;手机&#xff08;APP&#xff09;打开&#xff0c;内容显示更佳&#xff0c;不会的私聊博主即可 想要拿代码或加入学习计划&#xff08;** 博主会监督你并且教你写文章 **&#xff09;的拉到最下面&#xff08;PC端Web打开&#xff09;加博主即可&#xff0c;目…

【ArcGIS遇上Python】ArcGIS10.6 python批量将栅格中的特定值替换Setnull为NoData

案例一: 如下图所示为兰州市dem,将图一中高程大于1600m的像元值设置为无效(Setnull)之后的效果如图二所示。 实现过程: 栅格计算器参考文章:《【ArcGIS风暴】ArcGIS 10.2栅格计算器实用公式大全(经典珍藏版)》,该文章中主要以ArcGIS102.为平台讲解栅格计算器的…

开源力量:微软竟开源 PowerShell

导读曾经有段时间&#xff0c;微软称 Linux 是“癌症”&#xff0c;但是随着时光流逝&#xff0c;现在微软已经认识到了开源世界的不断增长&#xff0c;除了在这个领域加大投入之外别无选择。微软已经启动了几个开源项目&#xff0c;希望能吸引一些 Linux 用户&#xff0c;其中…

ENVI5.4完美实现MODIS NDVI数据格式转换和投影变换

如上图所示,分别为: View1:MODIS hdf数据多波段 View2:MODIS NDVI波段 View3:ArcGIS10.8投影变换后的MODIS NDVI View4:ENVI5.4投影变换后的MODIS NDVI 关于ArcGIS处理MODIS数据的操作,可以参照: 《ArcGIS10.8完美实现MODIS NDVI数据格式转换和投影变换》 《重磅!ArcG…

【前端就业课 第一阶段】HTML5 零基础到实战(九)列表

注意&#xff1a;手机&#xff08;APP&#xff09;打开&#xff0c;内容显示更佳&#xff0c;不会的私聊博主即可 想要拿代码或加入学习计划&#xff08;** 博主会监督你并且教你写文章 **&#xff09;的拉到最下面&#xff08;PC端Web打开&#xff09;加博主即可&#xff0c;目…

android 电源管理 wakelock 唤醒锁机制

Android 电源管理 — 唤醒锁全新上市 大多数人可能都遭遇过手机的电池续航时间较短带来的尴尬。 这极其令人讨厌。 没电的手机和一块水泥砖没什么差别。 一般而言&#xff0c;如果用户的手机电池无法持续一整天&#xff0c;他们会感到非常不满。而且&#xff0c;当手机充电时用…

初始Bootstrap

使用示例①下载Bootstrap框架 网址&#xff1a;http://v3.bootcss.com/getting-started/#download②解压得到三个文件③将文件添加进项目后&#xff0c;在页面中引用必要的css和js④查看效果&#xff08;a标签美化得不要不要的了&#xff5e;&#xff09;更多学习Bootstrap的资…

APP无埋点流程

最近无埋点技术很是流行&#xff0c;抽空研究了下诸葛IO&#xff0c;talkingData以及百分点这些业内知名公司的无埋点SDK&#xff0c;抽取其中重要的信息供大家参考&#xff1a;1、首先什么是无埋点呢&#xff0c;其实所谓无埋点就是开发者无需再对追踪点进行埋码&#xff0c;而…

Esri Maps For Office制作漂亮的地图

ArcGISOnline是一个基于云架构的资源中心,在这里你可以发布自己的地图资源、浏览其它ArcGIS爱好者发布的应用程序;总之,它为我们提供了一个在线交流的场所。 EsriMapsForOffice是ArcGISonline推出的一个Office环境的插件,可以让我们在Excel、Powerpoint中进行制图,就像在…

《什么是 eBPF》O'Reilly 报告中文版放送

祝大家端午安康&#xff0c;国泰民安&#xff0c;世界和平。今年端午节&#xff0c;鄙人人在北京&#xff0c;所在区有中风险地区&#xff0c;而老家又是所在省的唯一一个中风险地区&#xff0c;既出不了京&#xff0c;也回不了家。可谓有家不能回&#xff0c;真是每逢佳节倍思…

一键将Python2代码自动转化为Python3

Python2的代码直接在Python3环境运行的话会报错误&#xff1a; 如果大量的代码&#xff0c;无论是批量替换&#xff0c;还是逐行修改都够累的&#xff0c;这活儿表示不能干&#xff01;&#xff01;&#xff01; 有没有办法一键转换呢&#xff1f; 百度了一下发现网上的方法如…

【前端就业课 第一阶段】HTML5 零基础到实战(十)JavaScript基础一篇入门

注意&#xff1a;手机&#xff08;APP&#xff09;打开&#xff0c;内容显示更佳&#xff0c;不会的私聊博主即可 想要拿代码或加入学习计划&#xff08;** 博主会监督你并且教你写文章 **&#xff09;的拉到最下面&#xff08;PC端Web打开&#xff09;加博主即可&#xff0c;目…

一款开源的跨平台实时web应用框架——DotNetify

今天给大家介绍一个开源的轻量级跨平台实时HTMLC#.NET Web应用程序开发框架——DotNetify&#xff0c;允许你在C#.NET后端上创建具有React、React Native、Vue或Blazor 前端的实时、响应式、跨平台应用程序。它的主要特点是&#xff1a;简单且轻量响应式后端MVVM内置实时解决方…

【GeoDataBase】Geodatabase智能化操作:属性域

Geodatabase中所包含的不仅仅是要素类、要素集和表,还可能包含关系类、注释类、几何网络、拓扑等不同的结构和类别。 地理数据库按照面向对象的模型存储地理信息,也可以将其非空间信息保存在表中。对于要素和表可以设置一些规则进行限制,对属性的约束称为属性域。 属性域是描…

用Python写一个将Python2代码转换成Python3代码的批处理工具

之前写过一篇如何在windows操作系统上给.py文件添加一个快速处理的右键功能的文章&#xff1a;《一键将Python2代码自动转化为Python3》&#xff0c;作用就是为了将Python2的文件升级转换成Python3的文件。之后&#xff0c;有朋友问&#xff0c;如果有很多文件需要转换&#xf…

WP 手机Lumia 820 锁屏密码的POJI研究

Windows Phone lumia 手机锁屏密码的POJI研究大家好今天给大家分享一个最新研究案例&#xff0c;近日笔者Nokia Lumia 820&#xff0c;由于客户密码失误太多&#xff0c;导致锁屏23000余分钟&#xff0c;&#xff0c;请看&#xff1a;型号Nokia Lumia820条件&#xff1a;Lumia8…

ArcGIS10从入门到精通系列实验图文教程(附配套实验数据持续更新)

文章目录1. 专栏简介2. 专栏地址3. 专栏目录1. 专栏简介 本教程《ArcGIS从入门到精通系列实验教程》内容包括&#xff1a;ArcGIS平台简介、ArcGIS应用基础、空间数据的采集与组织、空间数据的转换与处理、空间数据的可视化表达、GIS空间分析导论、矢量数据的空间分析、栅格数据…

【iVX 初级工程师培训教程 10篇文拿证】09 聊天室制作

目录 【iVX 初级工程师培训教程 10篇文拿证】01 了解 iVX 完成新年贺卡 【iVX 初级工程师培训教程 10篇文拿证】02 数值绑定及自适应网站制作 【iVX 初级工程师培训教程 10篇文拿证】03 事件及猜数字小游戏 【iVX 初级工程师培训教程 10篇文拿证】04 画布及我和 iVX 合照 【iV…

为什么Dapr是比SpringCloud和Istio更优雅的微服务框架?

作者&#xff1a;徐磊文章首发地址&#xff1a;https://smartide.cn/zh/blog/2022-0601-dapr/Dapr 是微软主导的云原生开源项目&#xff0c;2019年10月首次发布&#xff0c;到正式发布 V1.0 版本的不到一年的时间内&#xff0c;github star 数达到了 1.2万&#xff08;现在已经…

Android之模拟网络请求返回http 502、400、401、402错误码

1 问题 app 网络框架协程没有做网络异常捕获处理&#xff0c;想本地测试网络接口&#xff0c;希望网络接口返回Http的错误码 比如502、400、401、402 2 解决办法 1、pc安装Fiddler Everywhere 2、让Fiddler Everywhere支持抓http和https的包 3、保持手机和电脑同一个局域网&am…