示例图:
一、技术选型:
1. 项目框架:MVP;注意:避免内存泄漏;
2. 图片加载: Universal-Image-Loader或Glide图片加载框架
3.网络加载框架:OkHttp
4. 自定义应用拦截器,封装公共请求参数(注意:必须通过拦截器封装公共请求参数,否则无法请求数据)
公共请求参数,在我们项目研发过程中,作用非常大。封装公共请求承参数之后,那么所有的接口都会默认携带这些公共参数,达到复用的效果。
公共请求参数:source=android
参数名称:source
参数值:android
类型:String
是否必传:是
5. 数据展示使用RecylerView;
二、业务逻辑需求:
1. 搜索框输入关键字(笔记本、手机),其他关键词没有数据,点击搜索实现如图效果;
2. 图1是列表布局,图2是网格布局;
3. 通过右上角按钮点击切换列表布局和网格布局,按钮随着切换;
4. 实现下拉刷新展示第一页数据、上拉加载更多实现分页功能。
三、接口:
http://120.27.23.105/product/searchProducts
keywords=笔记本&page=1
参数说明:
keywords 关键字字段 String类型 必传
page 页码数 String类型 必传
公共参数:source 来源字段 String类型 (通过自定义拦截器封装)
MainActivity.java
package com.bawei.com.wangruixin1509c20171113; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import com.bawei.com.wangruixin1509c20171113.adapter.NewsListAdapter; import com.bawei.com.wangruixin1509c20171113.bean.JavaBean; import com.bawei.com.wangruixin1509c20171113.httputils.INewsView; import com.bawei.com.wangruixin1509c20171113.presenter.NewsPresenter; import com.jwenfeng.library.pulltorefresh.BaseRefreshListener; import com.jwenfeng.library.pulltorefresh.PullToRefreshLayout; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity implements INewsView{ private RecyclerView re_view; private List<JavaBean.DataBean> list; private NewsListAdapter adapter; private Button btn_sousuo; private EditText et_sousuo; private NewsPresenter presenter; private ImageView right; private boolean flag = true; private PullToRefreshLayout mpull; private int page = 1; private String show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); re_view = (RecyclerView) findViewById(R.id.re_view); btn_sousuo = (Button) findViewById(R.id.btn_sousuo); et_sousuo = (EditText) findViewById(R.id.et_sousuo); right = (ImageView) findViewById(R.id.right); mpull = (PullToRefreshLayout) findViewById(R.id.mpull); list = new ArrayList<>(); adapter = new NewsListAdapter(this, list,1); LinearLayoutManager manager = new LinearLayoutManager(this); re_view.setLayoutManager(manager); re_view.setAdapter(adapter); presenter = new NewsPresenter(); presenter.attachView(this); btn_sousuo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { show = et_sousuo.getText().toString().trim(); presenter.getNews(show,page); } }); right.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (flag){ adapter.notifyDataSetChanged(); adapter = new NewsListAdapter(MainActivity.this, list,2); right.setImageDrawable(getResources().getDrawable(R.mipmap.lv_icon)); GridLayoutManager manager = new GridLayoutManager(MainActivity.this, 2); re_view.setLayoutManager(manager); re_view.setAdapter(adapter); flag = false; }else { adapter.notifyDataSetChanged(); adapter = new NewsListAdapter(MainActivity.this, list,1); right.setImageDrawable(getResources().getDrawable(R.mipmap.grid_icon)); LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this); re_view.setLayoutManager(manager); re_view.setAdapter(adapter); flag = true; } return false; } }); mpull.setLayoutMode(MODE_APPEND);//设置模板 mpull.setRefreshListener(new BaseRefreshListener() { @Override public void refresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { page=1; presenter.getNews(show,page); mpull.finishRefresh(); } }, 3000); } @Override public void loadMore() { new Handler().postDelayed(new Runnable() { @Override public void run() { page++; presenter.getNews(show,page); mpull.finishLoadMore(); } }, 3000 ); } }); } @Override public void success(String tag, List<JavaBean.DataBean> news) { list.clear(); list.addAll(news); adapter.notifyDataSetChanged(); adapter.setOnItemClickListener(new NewsListAdapter.OnItemClickListener() { @Override public void onClick(int position) { Intent intent = new Intent(MainActivity.this, Main2Activity.class); String url = list.get(position).getDetailUrl(); intent.putExtra("url",url); startActivity(intent); } @Override public void onLongClick(int position) { } }); } @Override public void failed(String tag, Exception e) { } }HttpUtils-get请求
package com.bawei.com.wangruixin1509c20171113.httputils; import android.os.Handler; import android.text.TextUtils; import com.bawei.com.wangruixin1509c20171113.callback.CallBack; import com.bawei.com.wangruixin1509c20171113.utils.GsonUtils; import java.io.IOException; import java.util.Map; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * Created by Wangrx on 2017/11/13. */ public class HttpUtils { private static Handler handler = new Handler(); private static volatile HttpUtils instance; private HttpUtils(){ } public static HttpUtils getInstance(){ if (null==instance){ synchronized (HttpUtils.class){ if (instance == null){ instance = new HttpUtils(); } } } return instance; } public void get(String url, Map<String,String> map, final CallBack callBack, final Class cls, final String tag){ if (TextUtils.isEmpty(url)){ return; } StringBuffer sb = new StringBuffer(); sb.append(url); if (url.contains("?")){ if (url.indexOf("?")==url.length()-1){ }else { sb.append("&"); } }else { sb.append("?"); } for (Map.Entry<String,String> entry:map.entrySet()){ sb.append(entry.getKey()) .append("=") .append(entry.getValue()) .append("&"); } if (sb.indexOf("&")!=-1){ sb.deleteCharAt(sb.lastIndexOf("&")); } OkHttpClient client = new OkHttpClient .Builder() .addInterceptor(new LoggingInterceptor()) .build(); Request request = new Request.Builder() .get() .url(sb.toString()) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, final IOException e) { handler.post(new Runnable() { @Override public void run() { callBack.onFailed(tag,e); } }); } @Override public void onResponse(Call call, Response response) throws IOException { final String result = response.body().string(); //请求成功后做解析,通过自己的回调接口将数据返回去 handler.post(new Runnable() { @Override public void run() { Object o; if (TextUtils.isEmpty(result)){ o = null; }else { o = GsonUtils.getInstance().fromJson(result, cls); } callBack.onSuccess(tag,o); } }); } }); } }
HttpUtils-post请求
package com.bwie.com.myapplication.httputils; import android.os.Handler; import android.util.Log; import com.bwie.com.myapplication.callback.CallBack; import com.bwie.com.myapplication.interceptor.LoggingInterceptor; import java.io.IOException; import java.util.Map; import okhttp3.Call; import okhttp3.FormBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * Created by Wangrx on 2017/11/21. */ public class HttpUtils { private static final String TAG = "HttpUtils"; private static volatile HttpUtils instance; private static Handler handler = new Handler(); private HttpUtils() { } public static HttpUtils getInstance() { if (null == instance) { synchronized (HttpUtils.class) { if (instance == null) { instance = new HttpUtils(); } } } return instance; } /** * 封装的post请求 * * @param url * @param map * @param callBack * @param cls */ public void post(String url, Map<String, String> map, final CallBack callBack, final Class cls, final String tag) { OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build(); FormBody.Builder builder = new FormBody.Builder(); for (Map.Entry<String, String> entry : map.entrySet()) { builder.add(entry.getKey(), entry.getValue()); } FormBody body = builder.build(); final Request request = new Request.Builder() .url(url) .post(body) .build(); Call call = client.newCall(request); call.enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, final IOException e) { Log.e(TAG, "onFailure: " + e.getCause().getStackTrace() + e.getMessage()); handler.post(new Runnable() { @Override public void run() { callBack.onFailed(tag,e); } }); } @Override public void onResponse(Call call, Response response) throws IOException { String result = response.body().string(); Log.i(TAG, "onResponse: " + result); final Object o = GsonUtils.getInstance().fromJson(result, cls); handler.post(new Runnable() { @Override public void run() { callBack.onSuccess(tag,o); } }); } }); } }
CallBack
package com.bawei.com.wangruixin1509c20171113.callback; /** * Created by Wangrx on 2017/11/13. */ public interface CallBack { void onSuccess(String tag,Object o); void onFailed(String tag,Exception e); }INewsView
package com.bawei.com.wangruixin1509c20171113.httputils; import com.bawei.com.wangruixin1509c20171113.bean.JavaBean; import java.util.List; /** * Created by Wangrx on 2017/11/13. */ public interface INewsView { void success(String tag, List<JavaBean.DataBean> list); void failed(String tag,Exception e); }LoggingInterceptor拦截器
package com.bawei.com.wangruixin1509c20171113.httputils; import java.io.IOException; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; /** * Created by 笔片 on 2017/10/16. * 网络拦截器 */ public class LoggingInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); HttpUrl url=original.url().newBuilder() .addQueryParameter("source","android") .build(); //添加请求头 Request request = original.newBuilder() .url(url) .build(); return chain.proceed(request); } }GsonUtils
package com.bawei.com.wangruixin1509c20171113.utils; import com.google.gson.Gson; /** * Created by Wangrx on 2017/11/13. */ public class GsonUtils { private static Gson instance; private GsonUtils(){ } public static Gson getInstance(){ if (instance==null){ instance = new Gson(); } return instance; } }NewsPresenter
package com.bawei.com.wangruixin1509c20171113.presenter; import com.bawei.com.wangruixin1509c20171113.bean.JavaBean; import com.bawei.com.wangruixin1509c20171113.callback.CallBack; import com.bawei.com.wangruixin1509c20171113.httputils.HttpUtils; import com.bawei.com.wangruixin1509c20171113.httputils.INewsView; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by Wangrx on 2017/11/13. */ public class NewsPresenter { private INewsView inv; public void attachView(INewsView inv){ this.inv = inv; } public void getNews(String show,int page){ Map<String, String> map = new HashMap<>(); map.put("keywords",show); map.put("page",page+""); HttpUtils.getInstance().get("http://120.27.23.105/product/searchProducts", map, new CallBack() { @Override public void onSuccess(String tag, Object o) { JavaBean bean = (JavaBean) o; if (bean != null){ List<JavaBean.DataBean> data = bean.getData(); inv.success(tag,data); } } @Override public void onFailed(String tag, Exception e) { inv.failed(tag,e); } }, JavaBean.class,"news"); } public void detachView(){ if (inv!=null){ inv = null; } } }NewsListAdapter
package com.bawei.com.wangruixin1509c20171113.adapter; import android.content.Context; import android.graphics.Paint; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.bawei.com.wangruixin1509c20171113.R; import com.bawei.com.wangruixin1509c20171113.bean.JavaBean; import com.bumptech.glide.Glide; import java.util.List; /** * Created by Wangrx on 2017/11/13. */ public class NewsListAdapter extends RecyclerView.Adapter<NewsListAdapter.ViewHolder> { private Context context; private List<JavaBean.DataBean> list; private int count; public NewsListAdapter(Context context, List<JavaBean.DataBean> list,int count) { this.context = context; this.list = list; this.count = count; } private OnItemClickListener mOnItemClickListener; public interface OnItemClickListener{ void onClick( int position); void onLongClick( int position); } public void setOnItemClickListener(OnItemClickListener onItemClickListener ){ this. mOnItemClickListener=onItemClickListener; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (count==1){ View v = View.inflate(context, R.layout.item, null); ViewHolder holder = new ViewHolder(v); return holder; }else if (count==2){ View v = View.inflate(context, R.layout.item1, null); ViewHolder holder = new ViewHolder(v); return holder; } return null; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { String[] split = list.get(position).getImages().split("[|]"); Glide.with(context).load(split[0]).into(holder.item_img); holder.item_bargainPrice.setText("原价:¥"+list.get(position).getBargainPrice()); holder.item_bargainPrice.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG); //下划线 holder.item_bargainPrice.getPaint().setAntiAlias(true);//抗锯齿 holder.item_price.setText("折扣价:¥"+list.get(position).getPrice()); holder.item_title.setText(list.get(position).getTitle()); if( mOnItemClickListener!= null){ holder.itemView.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onClick(position); } }); holder. itemView.setOnLongClickListener( new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { mOnItemClickListener.onLongClick(position); return false; } }); } } @Override public int getItemCount() { if (list==null){ return 0; } return list.size(); } public class ViewHolder extends RecyclerView.ViewHolder { private final ImageView item_img; private final TextView item_price; private final TextView item_bargainPrice; private final TextView item_title; public ViewHolder(View itemView) { super(itemView); item_img = itemView.findViewById(R.id.item_img); item_title = itemView.findViewById(R.id.item_title); item_price = itemView.findViewById(R.id.item_price); item_bargainPrice = itemView.findViewById(R.id.item_bargainPrice); } } }Main2Activity.java
package com.bawei.com.wangruixin1509c20171113; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; public class Main2Activity extends AppCompatActivity { private WebView web; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); Intent intent = getIntent(); String url = intent.getStringExtra("url"); web = (WebView) findViewById(R.id.web); web.loadUrl(url); web.setWebViewClient(new WebViewClient()); WebSettings settings = web.getSettings(); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setJavaScriptEnabled(true); } }activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bawei.com.wangruixin1509c20171113.MainActivity"> <TextView android:id="@+id/tv_show" android:layout_width="match_parent" android:layout_height="40dp" android:text="搜索商品" android:textSize="25sp" android:gravity="center" /> <ImageView android:id="@+id/right" android:layout_width="40dp" android:layout_height="40dp" android:src="@mipmap/grid_icon" android:layout_alignParentRight="true" /> <RelativeLayout android:id="@+id/rel" android:layout_width="match_parent" android:layout_height="50dp" android:layout_below="@+id/tv_show" android:layout_alignParentStart="true"> <EditText android:id="@+id/et_sousuo" android:layout_width="300dp" android:layout_height="50dp" android:drawableLeft="@mipmap/a_4" android:hint="请输入关键字" /> <Button android:id="@+id/btn_sousuo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/et_sousuo" android:text="搜索" /> </RelativeLayout> <com.jwenfeng.library.pulltorefresh.PullToRefreshLayout android:layout_width="match_parent" android:id="@+id/mpull" android:layout_below="@+id/rel" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/re_view" android:layout_width="match_parent" android:layout_height="wrap_content" ></android.support.v7.widget.RecyclerView> </com.jwenfeng.library.pulltorefresh.PullToRefreshLayout> </RelativeLayout>activity_main2.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bawei.com.wangruixin1509c20171113.Main2Activity"> <WebView android:id="@+id/web" android:layout_width="match_parent" android:layout_height="match_parent"></WebView> </RelativeLayout>item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:id="@+id/item_img" android:layout_width="100dp" android:layout_height="100dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="100dp" android:layout_toRightOf="@+id/item_img"> <TextView android:id="@+id/item_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:text="title" android:textSize="16sp" /> <TextView android:id="@+id/item_bargainPrice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/item_title" android:text="售价" android:layout_marginTop="10dp" android:textColor="#ccc" android:textSize="14sp" /> <TextView android:id="@+id/item_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/item_title" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/item_bargainPrice" android:text="售价" android:layout_marginTop="10dp" android:textColor="#f00" android:textSize="14sp" /> </RelativeLayout> </LinearLayout>权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />依赖:
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1' compile 'com.squareup.okhttp3:okhttp:3.9.0' compile 'com.google.code.gson:gson:2.8.2' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.jwenfeng.pulltorefresh:library:1.0.3'