效果图:
1.导入依赖
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.2' compile 'com.android.support:recyclerview-v7:25.3.1' compile 'com.squareup.okhttp3:okhttp:3.9.0' compile 'com.google.code.gson:gson:2.8.2' compile 'com.jakewharton:butterknife:7.0.0' compile 'com.liaoinstan.springview:library:1.3.0'2. 清单文件
(1)加权限:
<uses-permission android:name="android.permission.INTERNET" />
(2)配置application的name属性,ImageLoader初始化
<application
android:name=".util.BaseApplication"
</application>
一. 首页
1. RecyclerView条目信息页面 MainActivity
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.Toast; import com.dhw.lastmonthexam.adapter.MyAdapter; import com.dhw.lastmonthexam.bean.SongBean; import com.dhw.lastmonthexam.presenter.MyPresenter; import com.dhw.lastmonthexam.view.MyView; import com.liaoinstan.springview.container.DefaultFooter; import com.liaoinstan.springview.container.DefaultHeader; import com.liaoinstan.springview.widget.SpringView; import butterknife.Bind; import butterknife.ButterKnife; //RecyclerView条目信息页面 public class MainActivity extends AppCompatActivity implements MyView{ @Bind(R.id.recyclerView) RecyclerView recyclerView; @Bind(R.id.springView) SpringView springView; private MyAdapter adapter; private MyPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //关联p层 presenter = new MyPresenter(this); //设置线性布局管理器,布局适配器 adapter = new MyAdapter(this); LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); //设置SpringView的滑动监听事件 springView.setHeader(new DefaultHeader(this)); springView.setFooter(new DefaultFooter(this)); springView.setListener(new SpringView.OnFreshListener() { @Override public void onRefresh() { presenter.onRefresh(true); } @Override public void onLoadmore() { presenter.onRefresh(false); } }); presenter.onRefresh(true); } @Override public void success(SongBean songBean) { if (springView != null){ springView.onFinishFreshAndLoad(); } adapter.add(songBean.getSong_list()); } @Override public void failure(Exception e) { Toast.makeText(MainActivity.this, " error ", Toast.LENGTH_SHORT).show(); } }
2. 布局适配器
import android.content.Context; import android.content.Intent; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.dhw.lastmonthexam.R; import com.dhw.lastmonthexam.ShopActivity; import com.dhw.lastmonthexam.bean.SongBean; import com.dhw.lastmonthexam.util.ImageLoaderUtil; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; //布局适配器 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ItemViewHolder> { private Context context; private List<SongBean.SongListBean> list; public MyAdapter(Context context) { this.context = context; } //添加数据的方法 public void add(List<SongBean.SongListBean> list) { if (this.list == null) { this.list = new ArrayList<>(); } this.list.addAll(list); notifyDataSetChanged(); } @Override public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item,null); return new ItemViewHolder(view); } @Override public void onBindViewHolder(ItemViewHolder holder, final int position) { ImageLoader.getInstance().displayImage(list.get(position).getPic_radio(),holder.image, ImageLoaderUtil.getDefaultOption()); holder.title.setText(list.get(position).getTitle()); holder.author.setText(list.get(position).getAuthor()+" "+list.get(position).getTitle()); //条目单击事件,点击条目跳转到购物车页面 holder.image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (onItemClickListener != null){ onItemClickListener.onClick(v,position); } Intent intent = new Intent(context, ShopActivity.class); context.startActivity(intent); Toast.makeText(context,"当前单击条目为:"+position,Toast.LENGTH_SHORT).show(); } }); //条目长按事件 holder.image.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (onItemClickListener != null){ //回调接口,显示数据 onItemClickListener.onLongClick(v,position); } Toast.makeText(context,"当前长按条目为:"+position,Toast.LENGTH_SHORT).show(); return false; } }); } @Override public int getItemCount() { return list == null ? 0 : list.size(); } static class ItemViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.image) ImageView image; @Bind(R.id.title) TextView title; @Bind(R.id.author) TextView author; public ItemViewHolder(View itemView) { super(itemView); ButterKnife.bind(this,itemView); } } //再声明接口变量,设置接口的setter方法 private onItemClickListener onItemClickListener; public void setOnItemClickListener(onItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } //1.先条目的点击接口回调事件 public interface onItemClickListener{ public void onClick(View view,int position); //单击事件 public void onLongClick(View view,int position); //长按事件 } }3. 请求接口:返回的封装Bean类:(自己实现)
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.billboard.billList&type=1&size=10&offset=0
4. 本页面需要用到的mvp3层
(1)model层
import com.dhw.lastmonthexam.bean.SongBean; import com.dhw.lastmonthexam.okhttp.AbstractUiCallBack; import com.dhw.lastmonthexam.okhttp.OkhttpUtils; //Model层 public class MyModel { public void onRefresh(boolean up, final ModelCallBack callBack) { OkhttpUtils.getInstance01().asy(null, "http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.billboard.billList&type=1&size=10&offset=0", new AbstractUiCallBack<SongBean>() { @Override public void success(SongBean songBean) { callBack.success(songBean); } @Override public void failure(Exception e) { callBack.failure(e); } }); } public interface ModelCallBack{ public void success (SongBean songBean); public void failure (Exception e); } }(2)view层
import com.dhw.lastmonthexam.bean.SongBean; //View层 public interface MyView { public void success (SongBean songBean); public void failure (Exception e); }(3)presenter层
import com.dhw.lastmonthexam.bean.SongBean; import com.dhw.lastmonthexam.model.MyModel; import com.dhw.lastmonthexam.view.MyView; //presenter层 public class MyPresenter { private MyView view; private MyModel model; public MyPresenter(MyView view) { this.view = view; model = new MyModel(); } public void onRefresh(boolean up){ model.onRefresh(up, new MyModel.ModelCallBack() { @Override public void success(SongBean songBean) { view.success(songBean); } @Override public void failure(Exception e) { view.failure(e); } }); } }5. 布局设置:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.liaoinstan.springview.widget.SpringView android:id="@+id/springView" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView> </com.liaoinstan.springview.widget.SpringView> </RelativeLayout>
适配器 item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/image" android:layout_width="108dp" android:layout_height="100dp" /> <LinearLayout android:layout_height="100dp" android:orientation="vertical" android:layout_width="match_parent" > <TextView android:id="@+id/title" android:textSize="16sp" android:textStyle="bold" android:layout_weight="1" android:layout_height="0dp" android:gravity="center_vertical" android:layout_width="match_parent" /> <TextView android:id="@+id/author" android:textSize="13sp" android:layout_weight="1" android:layout_height="0dp" android:gravity="center_vertical" android:layout_width="match_parent" /> </LinearLayout> </LinearLayout>
二. 购物车页面:
1. ShopActivity
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; import com.dhw.lastmonthexam.bean.ShopBean; import com.dhw.lastmonthexam.presenter.MainPresenter; import com.dhw.lastmonthexam.view.MainViewListener; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; public class ShopActivity extends AppCompatActivity implements MainViewListener { @Bind(R.id.third_recyclerview) RecyclerView recyclerview; @Bind(R.id.third_allselect) CheckBox checkBoxAll; @Bind(R.id.third_totalprice) TextView AllPrice; @Bind(R.id.third_totalnum) TextView allNum; private MainPresenter presenter; private ShopAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_shop); ButterKnife.bind(this); presenter = new MainPresenter(this); presenter.getData(); //设置布局管理器及适配器 adapter = new ShopAdapter(this); LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); recyclerview.setLayoutManager(manager); recyclerview.setAdapter(adapter); adapter.setListener(new ShopAdapter.UpdateUiListener() { @Override public void setTotal(String total, String num, boolean allCheck) { checkBoxAll.setChecked(allCheck); allNum.setText("共计:"+num+" 件"); AllPrice.setText("总价:¥"+total); } }); } @Override public void success(ShopBean bean) { adapter.add(bean); } @Override public void failure(Exception e) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(ShopActivity.this, "error", Toast.LENGTH_SHORT).show(); } }); } @Override protected void onDestroy() { super.onDestroy(); presenter.detach(); } @OnClick(R.id.third_allselect) public void onViewClicked() { adapter.selectAll(checkBoxAll.isChecked()); } }2.适配器类 ShopAdapter
import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; import com.dhw.lastmonthexam.bean.ShopBean; import com.dhw.lastmonthexam.customview.PlusView; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import butterknife.Bind; import butterknife.ButterKnife; //页面适配器 public class ShopAdapter extends RecyclerView.Adapter<ShopAdapter.IViewHolder> { private Context context; private List<ShopBean.DataBean.ListBean> list; // 存放 商家的id 和 商家名称 private Map<String, String> map = new HashMap<>(); public ShopAdapter(Context context) { this.context = context; } /** * 添加数据 并更新显示 * @param bean */ public void add(ShopBean bean) { if (this.list == null) { this.list = new ArrayList<>(); } // 遍历商家 for (ShopBean.DataBean shop : bean.getData()) { map.put(shop.getSellerid(), shop.getSellerName()); // 遍历商品 for (int i = 0; i < shop.getList().size(); i++) { this.list.add(shop.getList().get(i)); } } setFirst(this.list); notifyDataSetChanged(); } /** * 设置数据源, 控制显示商家 * @param list */ private void setFirst(List<ShopBean.DataBean.ListBean> list) { if (list.size() > 0) { list.get(0).setIsFirst(1); for (int i = 1; i < list.size(); i++) { if (list.get(i).getSellerid() == list.get(i - 1).getSellerid()) { list.get(i).setIsFirst(2); } else { list.get(i).setIsFirst(1); } } } } @Override public IViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = View.inflate(context, R.layout.adapter_layout, null); return new IViewHolder(view); } @Override public void onBindViewHolder(final IViewHolder holder, final int position) { // 显示商品图片 if (list.get(position).getIsFirst() == 1) { //显示商家 holder.shop_checkbox.setVisibility(View.VISIBLE); holder.tvItemShopcartShopname.setVisibility(View.VISIBLE); holder.shop_checkbox.setChecked(list.get(position).isShopSelected()); // 显示商家的名称 // list.get(position).getSellerid() 取到商家的id // map.get()取到 商家的名称 holder.tvItemShopcartShopname.setText(map.get(String.valueOf(list.get(position).getSellerid()))); } else { holder.shop_checkbox.setVisibility(View.GONE); holder.tvItemShopcartShopname.setVisibility(View.GONE); } //控制 商品的 checkbox holder.item_checkbox.setChecked(list.get(position).isItemSelected()); String[] url = list.get(position).getImages().split("\\|"); ImageLoader.getInstance().displayImage(url[0], holder.item_pic); holder.item_name.setText(list.get(position).getTitle()); holder.item_price.setText("¥ "+list.get(position).getPrice() + ""); holder.plusViewId.setEditText(list.get(position).getNum()); // 商家的checkbox holder.shop_checkbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { list.get(position).setShopSelected(holder.shop_checkbox.isChecked()); for (int i = 0; i < list.size(); i++) { if (list.get(position).getSellerid() == list.get(i).getSellerid()) { list.get(i).setItemSelected(holder.shop_checkbox.isChecked()); } } notifyDataSetChanged(); sum(list); } }); // 商品的checkbox holder.item_checkbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { list.get(position).setItemSelected(holder.item_checkbox.isChecked()); for (int i = 0; i < list.size(); i++) { for (int j = 0; j < list.size(); j++) { if (list.get(i).getSellerid() == list.get(j).getSellerid() && !list.get(j).isItemSelected()) { list.get(i).setShopSelected(false); break; } else { list.get(i).setShopSelected(true); } } } notifyDataSetChanged(); sum(list); } }); holder.item_del.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { list.remove(position); setFirst(list); notifyDataSetChanged(); sum(list); } }); //加减号 holder.plusViewId.setListener(new PlusView.ClickListener() { @Override public void click(int count) { list.get(position).setNum(count); notifyDataSetChanged(); sum(list); } }); } @Override public int getItemCount() { return list == null ? 0 : list.size(); } /** * 计算总价 * @param list */ private void sum(List<ShopBean.DataBean.ListBean> list) { int totalNum = 0; float totalMoney = 0.0f; boolean allCheck = true; for (int i = 0; i < list.size(); i++) { if (list.get(i).isItemSelected()) { totalNum += list.get(i).getNum(); totalMoney += list.get(i).getNum() * list.get(i).getPrice(); } else { allCheck = false; } } listener.setTotal(totalMoney + "", totalNum + "", allCheck); } public void selectAll(boolean check) { for (int i = 0; i < list.size(); i++) { list.get(i).setShopSelected(check); list.get(i).setItemSelected(check); } notifyDataSetChanged(); sum(list); } static class IViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.shop_checkbox) CheckBox shop_checkbox; @Bind(R.id.tv_item_shopcart_shopname) TextView tvItemShopcartShopname; @Bind(R.id.item_checkbox) CheckBox item_checkbox; @Bind(R.id.item_pic) ImageView item_pic; @Bind(R.id.item_price) TextView item_price; @Bind(R.id.item_name) TextView item_name; @Bind(R.id.item_del) ImageView item_del; @Bind(R.id.plus_view_id) PlusView plusViewId; IViewHolder(View view) { super(view); ButterKnife.bind(this, view); } } public UpdateUiListener listener; public void setListener(UpdateUiListener listener) { this.listener = listener; } interface UpdateUiListener { public void setTotal(String total, String num, boolean allCheck); } }
3. 请求接口:返回的封装Bean类:(自己实现)
http://120.27.23.105/product/getCarts?uid=100(关键加上下面3个属性变量,设置setter和getter)
// 1 显示商家 2 隐藏商家 private int isFirst; // true 表示商家选中 false 相反 private boolean shopSelected; // true 表示 当前商品是选中的 false 相反 private boolean itemSelected;
4. 本页面需要用到的mvp 3层
(1)model层
import com.dhw.lastmonthexam.bean.ShopBean; import com.dhw.lastmonthexam.okhttp.AbstractUiCallBack; import com.dhw.lastmonthexam.okhttp.OkhttpUtils; public class MainModel { public void getData(final MainModelCallBack callBack){ OkhttpUtils.getInstance02().asy(null, "http://120.27.23.105/product/getCarts?uid=100", new AbstractUiCallBack<ShopBean>() { @Override public void success(ShopBean bean) { callBack.success(bean); } @Override public void failure(Exception e) { callBack.failure(e); } }); } }
需实现的接口类 MainModelCallBack :
import com.dhw.lastmonthexam.bean.ShopBean; public interface MainModelCallBack { public void success(ShopBean bean); public void failure(Exception e); }(2)view层
import com.dhw.lastmonthexam.bean.ShopBean; public interface MainViewListener { public void success(ShopBean bean); public void failure(Exception e); }(3)presenter层
import com.dhw.lastmonthexam.bean.ShopBean; import com.dhw.lastmonthexam.model.MainModel; import com.dhw.lastmonthexam.model.MainModelCallBack; import com.dhw.lastmonthexam.view.MainViewListener; public class MainPresenter { private MainViewListener listener; private MainModel mainModel; public MainPresenter(MainViewListener listener){ this.listener = listener ; this.mainModel = new MainModel(); } public void getData(){ mainModel.getData(new MainModelCallBack() { @Override public void success(ShopBean bean) { if(listener != null){ listener.success(bean); } } @Override public void failure(Exception e) { if(listener != null){ listener.failure(e); } } }); } /** * 防止内存泄漏 */ public void detach(){ listener = null; } }
5. 自定义加减器view
import android.content.Context; import android.support.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import com.dhw.lastmonthexam.R; /** * 购物车加减号 自定义View */ public class PlusView extends LinearLayout { private EditText editText; private Button revserse; private Button add; private int mCount = 1 ; public PlusView(Context context) { super(context); } public PlusView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); View view = LayoutInflater.from(context).inflate(R.layout.plus_layout,null,false); revserse = (Button) view.findViewById(R.id.revserse); add = (Button) view.findViewById(R.id.add); editText = (EditText) view.findViewById(R.id.content); revserse.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { //减号 try { String content = editText.getText().toString().trim() ; int count = Integer.valueOf(content); if(count > 1){ mCount = count-1; editText.setText(mCount+""); if(listener != null){ listener.click(mCount); } } } catch (NumberFormatException e) { e.printStackTrace(); } } }); add.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { //加号 try { String content = editText.getText().toString().trim() ; int count = Integer.valueOf(content)+1; mCount = count; editText.setText(count+""); if(listener != null){ listener.click(count); } } catch (NumberFormatException e) { e.printStackTrace(); } } }); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { } }); addView(view); } public PlusView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setEditText(int num){ if(editText != null){ editText.setText(num+""); } } public ClickListener listener; public void setListener(ClickListener listener){ this.listener = listener; } /** * 加减号 点击事件 */ public interface ClickListener { public void click(int count); } }
6. 设置本页面布局:
activity_shop.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/third_recyclerview" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_weight="0" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#FFFFFF" android:gravity="center_vertical" android:id="@+id/third_pay_linear" > <CheckBox android:drawablePadding="@dimen/padding_5dp" android:id="@+id/third_allselect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="全选" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" > <TextView android:id="@+id/third_totalprice" android:layout_width="200dp" android:layout_height="wrap_content" android:paddingLeft="@dimen/padding_10dp" android:paddingTop="@dimen/padding_10dp" android:text="总价:" android:textColor="@color/cblack" android:textSize="@dimen/common_font_size_16" /> <TextView android:textColor="@color/cblack" android:id="@+id/third_totalnum" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="@dimen/padding_10dp" android:text="共0件商品" android:textSize="@dimen/common_font_size_14" android:paddingBottom="@dimen/padding_10dp" /> </LinearLayout> <TextView android:id="@+id/third_submit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/login_btn" android:text="结算" android:paddingLeft="@dimen/margin_30dp" android:paddingRight="@dimen/margin_30dp" android:paddingTop="@dimen/padding_10dp" android:paddingBottom="@dimen/padding_10dp" android:layout_marginRight="@dimen/margin_10dp" /> </LinearLayout> </LinearLayout>适配器 adapter_layout.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="wrap_content" android:orientation="vertical" > <LinearLayout android:id="@+id/ll_shopcart_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <View android:background="@color/background_color" android:layout_height="@dimen/margin_10dp" android:layout_width="match_parent" android:id="@+id/view"/> <LinearLayout android:layout_height="wrap_content" android:layout_width="match_parent" android:gravity="center_vertical" > <!-- 商店checkbox --> <CheckBox android:id="@+id/shop_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="@dimen/margin_15dp" android:paddingRight="@dimen/margin_15dp" android:paddingTop="@dimen/margin_10dp" android:paddingBottom="@dimen/margin_10dp" /> <!-- 商店信息 --> <TextView android:drawableLeft="@drawable/shopcart_shop" android:id="@+id/tv_item_shopcart_shopname" android:textColor="@color/cblack" android:layout_width="match_parent" android:layout_height="wrap_content" android:drawablePadding="@dimen/padding_5dp" android:text="宝儿家服装" android:padding="@dimen/padding_10dp" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <View android:layout_width="match_parent" android:layout_height="@dimen/margin_1dp" android:background="@color/background_color" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" > <!-- 商品 checkbox --> <CheckBox android:id="@+id/item_checkbox" android:layout_marginLeft="18dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/margin_15dp" /> <!-- 商品图片 --> <ImageView android:id="@+id/item_pic" android:layout_width="60dp" android:layout_height="60dp" android:layout_margin="@dimen/margin_10dp" /> <LinearLayout android:layout_weight="1" android:layout_width="0dp" android:orientation="vertical" android:layout_height="wrap_content"> <TextView android:text="¥185" android:id="@+id/item_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/main_red_text" android:textSize="@dimen/common_font_size_14" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/margin_5dp" android:layout_marginBottom="@dimen/margin_5dp" > <TextView android:text="颜色:黑色" android:id="@+id/item_name" android:textColor="@color/cblack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="@dimen/common_font_size_12" /> </LinearLayout> <com.dhw.lastmonthexam.customview.PlusView android:layout_width="100dp" android:id="@+id/plus_view_id" android:layout_height="wrap_content" /> </LinearLayout> <View android:layout_width="@dimen/margin_1dp" android:layout_height="match_parent" android:background="@color/splitline_color" android:layout_marginTop="@dimen/padding_10dp" android:layout_marginBottom="@dimen/padding_10dp" /> <ImageView android:id="@+id/item_del" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/margin_20dp" android:src="@drawable/shopcart_delete" /> </LinearLayout> </LinearLayout> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="@dimen/margin_1dp" android:background="@color/background_color" /> </LinearLayout>自定义view的页面 plus_layout.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"> <Button android:layout_width="10dp" android:layout_height="wrap_content" android:id="@+id/revserse" android:text="-" android:background="#00FFFFFF"/> <EditText android:inputType="number" android:text="1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/content"/> <Button android:background="#00FFFFFF" android:text="+" android:id="@+id/add" android:layout_width="10dp" android:layout_height="wrap_content" /> </LinearLayout>7. 实现本页面所需xml配置文件(drawable文件夹下)
ic_launcher_background.xml
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportHeight="108" android:viewportWidth="108"> <path android:fillColor="#26A69A" android:pathData="M0,0h108v108h-108z" /> <path android:fillColor="#00000000" android:pathData="M9,0L9,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,0L19,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M29,0L29,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M39,0L39,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M49,0L49,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M59,0L59,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M69,0L69,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M79,0L79,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M89,0L89,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M99,0L99,108" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,9L108,9" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,19L108,19" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,29L108,29" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,39L108,39" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,49L108,49" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,59L108,59" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,69L108,69" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,79L108,79" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,89L108,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M0,99L108,99" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,29L89,29" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,39L89,39" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,49L89,49" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,59L89,59" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,69L89,69" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M19,79L89,79" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M29,19L29,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M39,19L39,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M49,19L49,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M59,19L59,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M69,19L69,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> <path android:fillColor="#00000000" android:pathData="M79,19L79,89" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8" /> </vector>login_btn.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="@dimen/height_200dp"></corners> <solid android:color="@color/pressed_icon_color"></solid> </shape>shopcart_add_btn.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="@dimen/height_200dp"></corners> <stroke android:color="@color/background_color" android:width="1dp"></stroke> </shape>所需小图片:
8. 在values文件夹下
(1)colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <color name="cwhite">#FFFFFF</color> <color name="title_bg">#FDE23D</color> <color name="tab_bg">#FFFFFF</color> <color name="tab_normal_textcolor">#373737</color> <color name="tab_selected_textcolor">#FDE23D</color> <color name="coffer">#442509</color> <color name="pressed_icon_color">#e53e42</color> <color name="background_color">#f6f6f6</color> <color name="main_red_text">#e53e42</color> <dimen name="padding_20dp">20dp</dimen> <color name="splitline_color">#dddddd</color> <color name="cblack">#000000</color> </resources>(2)dimens.xml
<resources> <dimen name="margin_10dp">10dp</dimen> <dimen name="padding_5dp">5dp</dimen> <dimen name="padding_10dp">10dp</dimen> <dimen name="common_font_size_16">16sp</dimen> <dimen name="common_font_size_14">14sp</dimen> <dimen name="height_200dp">200dp</dimen> <dimen name="margin_30dp">30dp</dimen> <dimen name="margin_15dp">15dp</dimen> <dimen name="margin_1dp">1dp</dimen> <dimen name="margin_5dp">5dp</dimen> <dimen name="common_font_size_12">12sp</dimen> <dimen name="padding_2dp">2dp</dimen> <dimen name="margin_20dp">20dp</dimen> </resources>
至此,页面写完。下面是公具类:
三. okhttp的单例封装以及拦截器:
1. OkhttpUtils
import java.io.File; import java.util.Map; import java.util.concurrent.TimeUnit; import okhttp3.FormBody; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; /** * Created by muhanxi on 17/11/10. * * * Okhttp 单例 范型的封装 */ public class OkhttpUtils { private static OkhttpUtils okhttpUtils = null ; private OkhttpUtils(){ } //Main页面 public static OkhttpUtils getInstance01(){ if(okhttpUtils == null){ okhttpUtils = new OkhttpUtils(); client = new OkHttpClient.Builder() .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20,TimeUnit.SECONDS) .connectTimeout(20,TimeUnit.SECONDS) .addInterceptor(new MyInterceptor()) .build(); } return okhttpUtils ; } //购物车页面 public static OkhttpUtils getInstance02(){ if(okhttpUtils == null){ okhttpUtils = new OkhttpUtils(); client = new OkHttpClient.Builder() .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20,TimeUnit.SECONDS) .connectTimeout(20,TimeUnit.SECONDS) //添加拦截器 .addInterceptor(new LoggingInterceptor()) .build(); } return okhttpUtils ; } private static OkHttpClient client ; /** * 发起异步请求 * @param params * @param url * @param callBack */ public void asy(Map<String,String> params,String url,AbstractUiCallBack callBack){ Request request = null ; if(params != null){ // post 请求 FormBody.Builder builder = new FormBody.Builder() ; for(Map.Entry<String,String> entry : params.entrySet()){ builder.add(entry.getKey(),entry.getValue()); } FormBody body = builder.build(); request = new Request.Builder() .url(url) .post(body) .build(); } else { // get 请求 request = new Request.Builder() .url(url) .build(); } client.newCall(request).enqueue(callBack); } private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); public static void postFile(Map<String,String> map, String url, File file,AbstractUiCallBack callBack){ String [] array = file.getAbsolutePath().split("\\/"); MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); for(Map.Entry<String,String> entry : map.entrySet()){ builder.addFormDataPart(entry.getKey(),entry.getValue()); } builder.addFormDataPart("imageFileName",array[array.length-1]); if(file.exists() && file.length() > 0){ builder.addFormDataPart("image",array[array.length-1], RequestBody.create(MEDIA_TYPE_PNG,file)); } MultipartBody body = builder.build() ; Request request = new Request.Builder() .url(url) .post(body) .build(); client.newCall(request).enqueue(callBack); } }2. AbstractUiCallBack
import android.os.Handler; import android.os.Looper; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; /** * Created by muhanxi on 17/11/10. * * * Okhttp 单例 范型的封装 */ public abstract class AbstractUiCallBack<T> implements Callback { /** * 成功回调 * @param t */ public abstract void success(T t); /** * 失败回调 * @param e */ public abstract void failure(Exception e); private Handler handler = null ; private Class clazz ; public AbstractUiCallBack(){ handler = new Handler(Looper.getMainLooper()); // 得到的是一个 AbstractUiCallBack<T> 的Type Type type = getClass().getGenericSuperclass() ; // 得到的是T的实际Type Type [] arr = ((ParameterizedType)type).getActualTypeArguments() ; clazz = (Class) arr[0] ; } @Override public void onFailure(Call call, final IOException e) { handler.post(new Runnable() { @Override public void run() { failure(e); } }); } @Override public void onResponse(Call call, Response response) throws IOException { try { if(response.code() == 200){ String result = response.body().string(); System.out.println("result = " + result); Gson gson = new Gson(); final T t = (T) gson.fromJson(result,clazz); handler.post(new Runnable() { @Override public void run() { success(t); } }); }else{ handler.post(new Runnable() { @Override public void run() { failure(new Exception()); } }); } } catch (final IOException e) { e.printStackTrace(); handler.post(new Runnable() { @Override public void run() { failure(e); } }); } catch (final JsonSyntaxException e) { e.printStackTrace(); handler.post(new Runnable() { @Override public void run() { failure(e); } }); } } }
3. 首页面请求数据使用的拦截器:MyInterceptor
import android.os.Build; import java.io.IOException; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; /** * 网络拦截器 */ public class MyInterceptor implements Interceptor { private static final String UA = "User-Agent"; @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request() .newBuilder() .addHeader(UA, makeUA()) .build(); return chain.proceed(request); } private String makeUA() { String s = Build.BRAND + "/" + Build.MODEL + "/" + Build.VERSION.RELEASE; return Build.BRAND + "/" + Build.MODEL + "/" + Build.VERSION.RELEASE; } }4. 购物车页面请求数据的拦截器:LoggingInterceptor
import java.io.IOException; import okhttp3.FormBody; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; /** * 可以实现 添加公共请求参数 */ public class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { //首先取到Request Request request = chain.request(); Response response = null; Request requestProcess = null ; if("GET".equals(request.method())){ String url = request.url().toString() + "&source=android"; Request.Builder builder = request.newBuilder() ; builder.get().url(url); requestProcess = builder.build(); response = chain.proceed(requestProcess); } else { FormBody.Builder builder = new FormBody.Builder() ; RequestBody requestBody = request.body() ; if(requestBody instanceof FormBody){ FormBody formBody = (FormBody)requestBody ; for (int i=0;i<formBody.size();i++){ builder.add(formBody.encodedName(i),formBody.encodedValue(i)); } builder.add("source","android"); } requestProcess = request.newBuilder().url(request.url().toString()).post(builder.build()).build() ; response = chain.proceed(requestProcess); } return response; } }
四. ImageLoader类的全局化配置初始化
1. BaseApplication.java
import android.app.Application; //全局初始化Application类 public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); //配置imageLoader ImageLoaderUtil.init(this); } }
2. ImageLoaderUtil.java
import android.content.Context; import android.graphics.Bitmap; import com.dhw.lastmonthexam.R; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache; import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.nostra13.universalimageloader.utils.StorageUtils; import java.io.File; public class ImageLoaderUtil { /** * 初始化imageLoader * @param context */ public static void init(Context context) { //1.获取配置config对象 File cacheDir = StorageUtils.getCacheDirectory(context); //缓存文件夹路径 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .threadPoolSize(3) // default 线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级 .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现 .memoryCacheSize(2 * 1024 * 1024) // 内存缓存的最大值 .memoryCacheSizePercentage(13) // default .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定义缓存路径 .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值 .diskCacheFileCount(100) // 可以缓存的文件数量 // default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密 .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) .imageDownloader(new BaseImageDownloader(context)) // default .imageDecoder(new BaseImageDecoder(true)) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() // 打印debug log .build(); //开始构建 //2.初始化配置...ImageLoader.getInstance()图片加载器的对象,单例模式 ImageLoader.getInstance().init(config); } /** * imageLoader加载图片的默认选项 * @return */ public static DisplayImageOptions getDefaultOption(){ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher) // 设置图片下载期间显示的默认图片 .showImageForEmptyUri(R.mipmap.ic_launcher) // 设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.mipmap.ic_launcher) // 设置图片加载或解码过程中发生错误显示的图片 .resetViewBeforeLoading(true) // default 设置图片在加载前是否重置、复位 .delayBeforeLoading(1000) // 下载前的延迟时间 .cacheInMemory(true) // default 设置下载的图片是否缓存在内存中 .cacheOnDisk(true) // default 设置下载的图片是否缓存在SD卡中 .considerExifParams(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565) // default 设置图片的解码类型 .displayer(new SimpleBitmapDisplayer()) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20) .build(); return options; } /** * imageLoader加载圆角图片....指定圆角的大小 * @return */ public static DisplayImageOptions getRoundedOption(int corner){ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher) // 设置图片下载期间显示的图片 .showImageForEmptyUri(R.mipmap.ic_launcher) // 设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.mipmap.ic_launcher) // 设置图片加载或解码过程中发生错误显示的图片 .resetViewBeforeLoading(true) // default 设置图片在加载前是否重置、复位 .delayBeforeLoading(1000) // 下载前的延迟时间 .cacheInMemory(true) // default 设置下载的图片是否缓存在内存中 .cacheOnDisk(true) // default 设置下载的图片是否缓存在SD卡中 .considerExifParams(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565) // default 设置图片的解码类型 .displayer(new RoundedBitmapDisplayer(corner)) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20) .build(); return options; } }
到此,全部代码实现完毕。