指尖菜谱App从0到1-精选模块实现(1)

本文详细介绍了一个App中的精选模块实现过程,包括使用聚合数据API获取微信精选内容、数据解析及封装、网络请求处理、数据加载通知机制及RecycleView数据展示等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面几篇相关文章介绍了,项目的准备、初建等相关工作。这篇将用于介绍精选模块的实现。看了很多供应商提供的接口如Mob、阿凡达数据以及聚合接口。最后决定使用聚合数据提供的微信精选api。
选择聚合数据理由:简单实用;数据每天都有更新;免费。
其接口的申请地址,及相关介绍请详情https://www.juhe.cn/docs/api/id/147
1、微信精选接口说明
接口地址:public static final String WEIXIN_CHOICE = “http://v.juhe.cn/weixin/query“;
接口返回的json数据如下:

{
    "reason": "success",
    "result": {
        "list": [
            {
                "id": "wechat_20150401071581",
                "title": "号外:集宁到乌兰花的班车出事了!!!!!",
                "source": "内蒙那点事儿",
                "firstImg": "http://zxpic.gtimg.com/infonew/0/wechat_pics_-214279.jpg/168",
                "mark": "",
                "url": "http://v.juhe.cn/weixin/redirect?wid=wechat_20150401071581"
            },
            {
                "id": "wechat_20150402028462",
                "title": "【夜读】梁晓声:你追求的,就是你人生的意义",
                "source": "人民日报",
                "firstImg": "http://zxpic.gtimg.com/infonew/0/wechat_pics_-214521.jpg/168",
                "mark": "",
                "url": "http://v.juhe.cn/weixin/redirect?wid=wechat_20150402028462"
            }
        ],
        "totalPage": 16,
        "ps": 20,
        "pno": 1
    },
    "error_code": 0
}

从数据中可以很轻易的看出每一个字段,其中list下的数组正是我们需要的数据。更加其我们可以封装出一个对象,以供Gson进行解析。
这里起名为JuheWXChoice,具体代码如下

public class JuheWXChoice {
    private int error_code;//返回码
    private String reason;//返回说明
    private Result result;//结果集
    /********get & set************/
    ...
    public String getReason() {
        return reason;
    }

    public class Result {
        List<Content> list;
    /********get & set************/
    ...
    }

    public class Content {
        private String id;
        private String title;
        private String source;//来源
        private String firstImg;//缩略图
        private String mark;
        private String url;

      /********get & set************/
    ...
        @Override
        public String toString() {
            return "Content{" +
                    "id='" + id + '\'' +
                    ", title='" + title + '\'' +
                    ", source='" + source + '\'' +
                    ", firstImg='" + firstImg + '\'' +
                    ", mark='" + mark + '\'' +
                    ", url='" + url + '\'' +
                    '}';
        }
    }
}

注意点:封装的对象属性名一定要与json数据的字段一致,private Result result属性的‘result’与json数据的‘result’相对应,如果把名字一变,将会出现解析异常。当然分装的对象中可以少,也就是说private String id;去了,依旧可以解析,只不过没有对象中没有id这属性了。

2、通过接口进行数据加载
更加接口的说明我们在进行get时需要传入的参数分别为key与pno,其它默认就行。
再此之前我们新建一个NetAction类,用于App中的所有数据加载工作,当然相关的加载我们还是得使用之前封装好的NetUtils类详情请看http://blog.youkuaiyun.com/it_faquir/article/details/52982494介绍。

 public void getJuheWXChoice(String page, final List<JuheWXChoice.Content> choice) {
        Map<String, String> param = new HashMap<>();
        param.put("key", "" + MyApplication.JUHE_KEY);
        param.put("pno", "" + page);//第几页
        NetUtils.get(NetUri.JUHE.WEIXIN_CHOICE, param, new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int i) {

            }

            @Override
            public void onResponse(String s, int i) {
                Gson gson = new Gson();
                JuheWXChoice juheWXChoice = gson.fromJson(s, JuheWXChoice.class);
                choice.addAll(juheWXChoice.getResult().getList());
                netLoadings.get(KUID_CHOICE_LIST).netLoadingComp(KUID_CHOICE_LIST);
            }
        });
    }

我们会发现其中有个netLoadings.get(KUID_CHOICE_LIST).netLoadingComp(KUID_CHOICE_LIST);,这个的实现目的是为了在加载完数据后通知界面进行相应的更新。其实也这里用了观察者模式。
首先写一个接口,专门用于此。如下:

/**
 * 观察者模式网络加载专用接口,kuid作为某个事件的一个id。
 */
public interface NetLoadingListener {
    void netLoadingComp(int kuid);
}

你会好奇,kuid是干嘛的,其实作者目的是为了能够使此接口适用于不同环境的网络加载中,以kuid作为唯一标识,而不用重复写多个类似的接口。

在NetAction中我们需要如下Map用于存放NetLoadingListener,便可应用中kuid来找到对象的NetLoadingListener,进行相应的事件通知。

 private static Map<Integer, NetLoadingListener> netLoadings = new HashMap<>();

    //添加网络加载事件
    public static void addNetLoadingListener(int kuid, NetLoadingListener listener) {
        netLoadings.put(kuid, listener);
        System.out.println("add ok");
    }

在ChoiceFragment的onCreate中实现实现相应的接口

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //添加监听器
        NetAction.addNetLoadingListener(NetAction.KUID_CHOICE_LIST, this);
        netAction = NetAction.newAction();
        netAction.getJuheWXChoice("" + curPager, choiceList);
    }
    /**
     * 监听器触发处,网络数据获取完成,触发此
     * @param kuid
     */
    @Override
    public void netLoadingComp(int kuid) {
        if (kuid == NetAction.KUID_CHOICE_LIST) {
            mAdapter.notifyDataSetChanged();
        }
    }

当数据加载成功后,netLoadingComp方法便被调用,此此时的List < JuheWXChoice.Content> choiceList引用的对象便不为空,且有数据,我们即可进行正常的显示数据。
3、利用RecycleView进行数据显示
根据加载的数据,我们可以设计item需要显示的内容:
这里写图片描述

因此我们可以对此进行相应的界面编写

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/choice_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorWhite"
    android:clickable="true"
    android:elevation="2dp"
    android:orientation="vertical"
    android:padding="5dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/choice_list_img"
            android:layout_width="80dp"
            android:layout_height="80dp" />

        <TextView
            android:id="@+id/choice_list_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="10dp"
            android:layout_toEndOf="@+id/choice_list_img"
            android:layout_toRightOf="@+id/choice_list_img"
            android:lines="3"
            android:text="@string/app_name"
            android:textColor="@color/colorBlack"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/choice_list_source"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_marginRight="10dp"
            android:text="@string/app_name"
            android:textSize="12sp" />
    </RelativeLayout>
</LinearLayout>

在ChoiceFragment中进行相应的RecycleAdapter的编写与RecycleView显示即可,完整代码如下

/**
 * Created by hgs on 2016/10/26.
 */
public class ChoiceFragment extends BaseFragment implements NetLoadingListener, MySwipe.OnUpLoadingListener, SwipeRefreshLayout.OnRefreshListener {
    private static ChoiceFragment choiceFragment;
    //数据
    private List<JuheWXChoice.Content> choiceList = new ArrayList<>();
    private NetAction netAction;
    private int curPager = 1;//记录当前所显示的页
    private ObjectAnimator animation;
    private ChoiceRecycleAdapter mAdapter;


    @BindView(R.id.main_bar_title)
    TextView tvTitle;
    @BindView(R.id.choice_recycle)
    RecyclerView cRecycle;
    @BindView(R.id.choice_mySwipe)
    MySwipe mySwipe;
    @BindView(R.id.loading_layout)
    View loadingLayout;
    @BindView(R.id.loading_icon)
    ImageView loadingIcon;

    public static ChoiceFragment newInstance() {
        if (choiceFragment == null) {
            choiceFragment = new ChoiceFragment();
        }
        return choiceFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //添加监听器
        NetAction.addNetLoadingListener(NetAction.KUID_CHOICE_LIST, this);
        netAction = NetAction.newAction();
        netAction.getJuheWXChoice("" + curPager, choiceList);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_choice, container, false);
        ButterKnife.bind(this, view);
        initView();
        return view;
    }

    private void initView() {
        tvTitle.setText("指尖精选");
        //recycleView
        cRecycle.setLayoutManager(new LinearLayoutManager(getContext()));
        mAdapter = new ChoiceRecycleAdapter(choiceList);
        cRecycle.setAdapter(mAdapter);
    }

    /**
     * 监听器触发处,网络数据获取完成,触发此
     *
     * @param kuid
     */
    @Override
    public void netLoadingComp(int kuid) {
        if (kuid == NetAction.KUID_CHOICE_LIST) {
            mAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public void scrollMoving(int scrollY, int distance) {
        if (loadingLayout != null) {
            loadingLayout.setVisibility(View.VISIBLE);
            showUpLoadingAnim(loadingIcon);
        }
    }
    class ChoiceRecycleAdapter extends RecyclerView.Adapter<MyAdapterHolder> {
        List<JuheWXChoice.Content> choiceList;

        ChoiceRecycleAdapter(List<JuheWXChoice.Content> choiceList) {
            this.choiceList = choiceList;
        }

        @Override
        public MyAdapterHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(getContext()).inflate(R.layout.choice_list_layout, null);
            return new MyAdapterHolder(view);
        }

        @Override
        public void onBindViewHolder(MyAdapterHolder holder, int position) {
            String imgUri = choiceList.get(position).getFirstImg();
            if (imgUri == null || imgUri.isEmpty()) {
                holder.listImg.setVisibility(View.GONE);
            } else
                netAction.loadImage(getContext(), choiceList.get(position).getFirstImg(), holder.listImg);
            holder.listSource.setText("" + choiceList.get(position).getSource());
            holder.listTitle.setText("" + choiceList.get(position).getTitle());
            holder.setItemClickListener(choiceList.get(position).getUrl());
        }

        @Override
        public int getItemCount() {
            return choiceList.size();
        }

    }

    class MyAdapterHolder extends RecyclerView.ViewHolder{
        @BindView(R.id.choice_list_img)
        ImageView listImg;
        @BindView(R.id.choice_list_title)
        TextView listTitle;
        @BindView(R.id.choice_list_source)
        TextView listSource;
        @BindView(R.id.choice_list)
        LinearLayout listLayout;

        public MyAdapterHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
        }
}

需要注意的是,作者在这里使用了ButterKnife框架,通过注解的方式来简化了findViewById的方式,具体详情https://github.com/JakeWharton/butterknife
好了我们来看看效果如何吧。
这里写图片描述

不过你会发现没法进行下拉刷新与上拉加载以及item的点击,查看详细的内容。后续请关注指尖菜谱App从0到1-精选模块实现(2)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值