购物车+删除条目

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
   >


   <TextView
       android:background="@color/colorPrimary"
       android:text="购物车"
       android:textSize="20dp"
       android:textColor="#FFFFFF"
       android:textStyle="bold"
       android:gravity="center"
       android:layout_width="match_parent"
       android:layout_height="40dp" />


    <ExpandableListView
        android:id="@+id/expandView"
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:groupIndicator="@null"
        android:layout_height="0dp"/>


    <View
        android:layout_width="match_parent"
        android:layout_height="0.3dp"
        android:background="#30b0b0b0"/>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp">


        <CheckBox
            android:text="全选"
            android:id="@+id/allSelect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />




        <View
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="1dp"/>


        <TextView
            android:gravity="center"
            android:text="jiage"
            android:id="@+id/price"
            android:textSize="16sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />


        <TextView
            android:gravity="center"
            android:textColor="#FFFFFF"
            android:textStyle="bold"
            android:background="#FF0000"
            android:layout_marginLeft="20dp"
            android:text="jiage"
            android:id="@+id/pay"
            android:layout_width="100dp"
            android:layout_height="match_parent" />


    </LinearLayout>


</LinearLayout>

父布局

<?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">


    <CheckBox
        android:id="@+id/parent_cb"
        android:focusable="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/parent_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

子布局

<?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:gravity="center_vertical">


    <CheckBox
        android:id="@+id/child_cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/child_drawee"
        android:layout_width="60dp"
        android:layout_height="80dp" />


    <LinearLayout
        android:layout_marginLeft="10dp"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="vertical">


        <TextView
            android:id="@+id/child_title"
            android:lines="1"
            android:singleLine="true"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />


        <TextView
            android:id="@+id/child_date"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />


        <LinearLayout
            android:layout_marginRight="10dp"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">


            <TextView
                android:id="@+id/child_price"
                android:layout_width="0dp"
                android:gravity="left"
                android:layout_height="wrap_content"
                android:layout_weight="1" />


            <TextView
                android:gravity="right"
                android:id="@+id/child_num"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />


        </LinearLayout>


    </LinearLayout>

</LinearLayout>

自定义加减布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:layout_width="100dp"
    android:layout_height="100dp">




    <TextView
        android:background="@drawable/bian"
        android:gravity="center"
        android:id="@+id/jian"
        android:text="-"
        android:textSize="20dp"
        android:textStyle="bold"
        android:layout_width="50dp"
        android:layout_height="30dp" />


    <TextView
        android:background="@drawable/bian"
        android:id="@+id/num"
        android:gravity="center"
        android:text="10"
        android:textSize="20dp"
        android:textStyle="bold"
        android:layout_width="60dp"
        android:layout_height="30dp" />


    <TextView
        android:background="@drawable/bian"
        android:id="@+id/add"
        android:gravity="center"
        android:text="+"
        android:textSize="20dp"
        android:textStyle="bold"
        android:layout_width="50dp"
        android:layout_height="30dp" />

</LinearLayout>

http包

URL地址

public class ApiConfig {
    public static final String baseUrl = "https://www.zhaoapi.cn/";
    public static final String index = "ad/getAd";
    public static final String cart = "product/getCarts";
    public static final String updateCart = "product/updateCarts";

}

服务类

public interface ApiService {
    @GET("{url}")
    Observable<ResponseBody> doGet(@Path(value = "url",encoded = true) String url, @QueryMap Map<String,String>map);


    @FormUrlEncoded
    @POST("{url}")
    Observable<ResponseBody> doPost(@Path(value = "url",encoded = true) String url, @FieldMap Map<String,String>map);

}

工具类

public class RetrofitUtil {
    private static ApiService apiService;




    private RetrofitUtil(){


    }


    public static ApiService getApiService(String baseUrl){
        if(apiService == null){
            synchronized (RetrofitUtil.class){
                if(apiService == null){
                    initApiService(baseUrl);
                }
            }
        }
        return apiService;
    }


    private static void initApiService(String baseUrl){
         apiService = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build().create(ApiService.class);
    }


}

view包

public interface IView {


    void succeed(String json);
    void error(Throwable throwable);

}

二级列表适配器

public class ExpandAdapter extends BaseExpandableListAdapter {
    private CartBean cartBean;
    private Context context;
    private AlertDialog alertDialog;
    private View alerView;
    private int count;
    private TextView alertNumView;


    public ExpandAdapter(CartBean cartBean, Context context) {
        this.cartBean = cartBean;
        this.context = context;
        initDialog();
    }


    @Override
    public int getGroupCount() {
        return cartBean.getData().size();
    }


    @Override
    public int getChildrenCount(int groupPosition) {
        return cartBean.getData().get(groupPosition).getList().size();
    }


    @Override
    public Object getGroup(int groupPosition) {
        return null;
    }


    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return null;
    }


    @Override
    public long getGroupId(int groupPosition) {
        return 0;
    }


    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return 0;
    }


    @Override
    public boolean hasStableIds() {
        return false;
    }


    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        if(convertView == null){
            convertView = View.inflate(context, R.layout.parent_view,null);
        }
        final CheckBox cb =convertView.findViewById(R.id.parent_cb);
        TextView name = convertView.findViewById(R.id.parent_name);


        final CartBean.DataBean dataBean = cartBean.getData().get(groupPosition);
        String sellerName = dataBean.getSellerName();


        cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean checked = cb.isChecked();
                setGoodsCheckState(dataBean.getList(),checked);
            }
        });
        boolean checked = dataBean.isChecked();


        name.setText(sellerName);
        cb.setChecked(checked);
        return convertView;
    }


    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        if(convertView == null){
            convertView = View.inflate(context, R.layout.child_view,null);
        }
        final CheckBox cb =convertView.findViewById(R.id.child_cb);
        TextView date = convertView.findViewById(R.id.child_date);
        TextView price = convertView.findViewById(R.id.child_price);
        TextView title = convertView.findViewById(R.id.child_title);
        SimpleDraweeView draweeView = convertView.findViewById(R.id.child_drawee);
        TextView numView = convertView.findViewById(R.id.child_num);


        final CartBean.MyBean myBean = cartBean.getData().get(groupPosition).getList().get(childPosition);
        double bargainPrice = myBean.getBargainPrice();
        String createtime = myBean.getCreatetime();
        final int num = myBean.getNum();
        String iconUrl  = myBean.getImages().split("\\|")[0];
        int selected = myBean.getSelected();
        String titleStr = myBean.getTitle();
        numView.setText("×"+num);


        cb.setChecked(selected == 1?true:false);
        date.setText(createtime);
        price.setText(bargainPrice+"元");
        title.setText(titleStr);
        draweeView.setImageURI(Uri.parse(iconUrl));


        cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean checked = cb.isChecked();
                m.clear();
                int num = myBean.getNum();
                int sellerid = myBean.getSellerid();
                int pid = myBean.getPid();
                m.put("uid","71");
                m.put("pid",""+pid);
                m.put("sellerid",sellerid+"");
                m.put("num",""+num);
                int se = checked ? 1 : 0;
                m.put("selected",""+se);
                updateCart();
            }
        });


        convertView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                alertNumView.setText(num+"");
                count = num;
                alertDialog.show();
                m.clear();
                int num = myBean.getNum();
                int sellerid = myBean.getSellerid();
                int pid = myBean.getPid();
                m.put("uid","71");
                m.put("pid",""+pid);
                m.put("sellerid",sellerid+"");
                m.put("selected",myBean.getSelected()+"");
                return false;
            }
        });


        return convertView;
    }


    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }


    private Map<String,String> m = new HashMap<>();


    public void setGoodsCheckState(List<CartBean.MyBean> list,boolean flag){
        Log.e("Flag",flag+"");
        for (int i = 0; i < list.size(); i++) {
            CartBean.MyBean myBean = list.get(i);
            m.clear();
            int num = myBean.getNum();
            int sellerid = myBean.getSellerid();
            int pid = myBean.getPid();
            m.put("uid","71");
            m.put("pid",""+pid);
            m.put("sellerid",sellerid+"");
            m.put("num",""+num);
            int se = flag ? 1 : 0;
            m.put("selected",""+se);
            updateCart();
        }


    }


    public void updateCart(){
        RetrofitUtil.getApiService(ApiConfig.baseUrl)
                .doGet(ApiConfig.updateCart,m)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {


                    }


                    @Override
                    public void onNext(ResponseBody responseBody) {
                        try {
                            Log.e("resss",responseBody.string());


                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally {
                            CartActivity.goNet();
                        }
                    }


                    @Override
                    public void onError(Throwable e) {


                    }


                    @Override
                    public void onComplete() {


                    }
                });
    }




    public void initDialog(){
        alerView = View.inflate(context, R.layout.alert_view,null);
        TextView jian = alerView.findViewById(R.id.jian);
        alertNumView = alerView.findViewById(R.id.num);
        TextView add = alerView.findViewById(R.id.add);


        jian.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(count>0){
                    count--;
                }else{


                }
                alertNumView.setText(""+count);
            }
        });


        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                count++;
                alertNumView.setText(""+count);
            }
        });




        alertDialog = new AlertDialog.Builder(context)
                .setTitle("修改购买数量")
                .setView(alerView)
                .setNegativeButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        m.put("num",count+"");
                        updateCart();
                    }
                })
                .setPositiveButton("取消", null)
                .create();
    }

}

public class MainActivity extends AppCompatActivity implements IView {
    @BindView(R.id.mBanner)
    MBanner mBanner;
    @BindView(R.id.seckill_recycler)
    RecyclerView seckillRecycler;
    @BindView(R.id.recommend_recycler)
    RecyclerView recommendRecycler;
    private Unbinder bind;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bind = ButterKnife.bind(this);


        PresenterImpl presenter = new PresenterImpl(this);
        presenter.getDataFromNet(ApiConfig.index, null);


        seckillRecycler.setLayoutManager(new LinearLayoutManager(this, OrientationHelper.HORIZONTAL, false));
        recommendRecycler.setLayoutManager(new CustomLinearLayoutManager(this,2));
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        bind.unbind();
    }


    @Override
    public void succeed(String json) {
        IndexBean indexBean = new Gson().fromJson(json, IndexBean.class);


        ArrayList<String> list = new ArrayList<>();


        for (int i = 0; i < indexBean.getData().size(); i++) {
            IndexBean.DataBean dataBean = indexBean.getData().get(i);
            list.add(dataBean.getIcon());
        }
        mBanner.setBannerData(list);
        seckillRecycler.setAdapter(new SeckillAdapter(indexBean.getMiaosha(), this));
        recommendRecycler.setAdapter(new RecommendAdapter(indexBean.getTuijian(),this));
    }


    @Override
    public void error(Throwable throwable) {
        Toast.makeText(this, throwable.toString(), Toast.LENGTH_SHORT).show();
    }

}

public class CartActivity extends AppCompatActivity implements IView {
    private List<CartBean.MyBean> maxList;
    private double totalPrice;
    private int totalCount;
    @BindView(R.id.expandView)
    ExpandableListView expandView;
    @BindView(R.id.allSelect)
    CheckBox allSelect;
    @BindView(R.id.price)
    TextView price;
    @BindView(R.id.pay)
    TextView pay;
    private Unbinder bind;
    private static PresenterImpl presenter;
    private int index;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cart);
        bind = ButterKnife.bind(this);


        presenter = new PresenterImpl(this);
        goNet();
        expandView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                return true;//返回true,表示不可点击
            }
        });
    }


    @OnClick({R.id.allSelect, R.id.pay})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.allSelect:
                boolean flag = allSelect.isChecked();
                index = 0;
                updataAllState(flag);
                break;
            case R.id.pay:
                break;
        }
    }




    @Override
    public void succeed(String json) {
        CartBean cartBean = new Gson().fromJson(json, CartBean.class);


        List<CartBean.MyBean> list = cartBean.getData().get(0).getList();
        if (list == null || list.size() < 1) {
            cartBean.getData().remove(0);
        }


        setGroudChecked(cartBean);
        allSelect.setChecked(isAllChecked(cartBean));
        totalPrice();


        java.text.DecimalFormat   df=new   java.text.DecimalFormat("0.00");
        String format = df.format(totalPrice)+"元";
        pay.setText("去结算("+totalCount+")");
        price.setText("合计:"+format);
        ExpandAdapter expandAdapter = new ExpandAdapter(cartBean, this);
        expandView.setAdapter(expandAdapter);


        for (int i = 0; i < expandAdapter.getGroupCount(); i++) {
            expandView.expandGroup(i);
        }




    }


    @Override
    public void error(Throwable throwable) {


    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        bind.unbind();
    }




    public void setGroudChecked(CartBean cartBean) {
        for (int i = 0; i < cartBean.getData().size(); i++) {
            CartBean.DataBean dataBean = cartBean.getData().get(i);
            boolean flag = checkGroudisChecked(dataBean.getList());
            dataBean.setChecked(flag);
        }
    }


    public boolean checkGroudisChecked(List<CartBean.MyBean> list) {
        for (int i = 0; i < list.size(); i++) {
            int selected = list.get(i).getSelected();
            Log.e(selected + "", "AA");
            if (selected != 1) {
                return false;
            }
        }
        return true;
    }


    public static void goNet() {
        HashMap<String, String> map = new HashMap<>();
        map.put("uid", "71");
        presenter.getDataFromNet(ApiConfig.cart, map);
    }


    public boolean isAllChecked(CartBean cartBean) {


        maxList = new ArrayList<>();
        for (int i = 0; i < cartBean.getData().size(); i++) {
            maxList.addAll(cartBean.getData().get(i).getList());
        }


        for (int i = 0; i < maxList.size(); i++) {
            int selected = maxList.get(i).getSelected();
            if (selected != 1) {
                return false;
            }
        }
        return true;
    }


    public void updataAllState(final boolean flag){
        Map<String,String> m = new HashMap<>();
        CartBean.MyBean myBean = maxList.get(index);
        String title = myBean.getTitle();
        Log.e("TAG",title);
        Log.e("msx",maxList.size()+"");
        int num = myBean.getNum();
        int sellerid = myBean.getSellerid();
        int pid = myBean.getPid();
        m.put("uid","71");
        m.put("pid",""+pid);
        m.put("sellerid",sellerid+"");
        m.put("num",""+num);
        int se = flag ? 1 : 0;
        m.put("selected",""+se);


        RetrofitUtil.getApiService(ApiConfig.baseUrl)
                .doGet(ApiConfig.updateCart,m)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {


                    }


                    @Override
                    public void onNext(ResponseBody responseBody) {
                        try {
                            Log.e("resss",responseBody.string());


                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if(index == maxList.size()){
                            goNet();
                        }else{
                            Log.e("index",index+"");
                            updataAllState(flag);
                        }
                        index++;
                    }


                    @Override
                    public void onError(Throwable e) {


                    }


                    @Override
                    public void onComplete() {


                    }
                });


    }


    public void totalPrice(){
        totalPrice = 0;
        totalCount = 0;


        for (int i = 0; i < maxList.size(); i++) {
            CartBean.MyBean myBean = maxList.get(i);
            if(myBean.getSelected() == 1){
                double bargainPrice = myBean.getBargainPrice();
                int num = myBean.getNum();
                double price = bargainPrice * num;
                totalPrice+=price;
                totalCount+=num;
            }
        }


    }


}

model层

bean类

public class CartBean {
 
    private String msg;
    private String code;
    private List<DataBean> data;


    public String getMsg() {
        return msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public String getCode() {
        return code;
    }


    public void setCode(String code) {
        this.code = code;
    }


    public List<DataBean> getData() {
        return data;
    }


    public void setData(List<DataBean> data) {
        this.data = data;
    }


    public static class DataBean {
        /**
         * list : []
         * sellerName :
         * sellerid : 0
         */




        private String sellerName;
        private String sellerid;
        private List<MyBean> list;
        private boolean isChecked;


        public boolean isChecked() {
            return isChecked;
        }


        public void setChecked(boolean checked) {
            isChecked = checked;
        }
        public String getSellerName() {
            return sellerName;
        }


        public void setSellerName(String sellerName) {
            this.sellerName = sellerName;
        }


        public String getSellerid() {
            return sellerid;
        }


        public void setSellerid(String sellerid) {
            this.sellerid = sellerid;
        }


        public List<MyBean> getList() {
            return list;
        }


        public void setList(List<MyBean> list) {
            this.list = list;
        }
    }


    public static class MyBean{
        private double bargainPrice;
        private String createtime;
        private String detailUrl;
        private String images;
        private int num;
        private int pid;
        private double price;
        private int pscid;
        private int selected;
        private int sellerid;
        private String subhead;
        private String title;


        public double getBargainPrice() {
            return bargainPrice;
        }


        public void setBargainPrice(double bargainPrice) {
            this.bargainPrice = bargainPrice;
        }


        public String getCreatetime() {
            return createtime;
        }


        public void setCreatetime(String createtime) {
            this.createtime = createtime;
        }


        public String getDetailUrl() {
            return detailUrl;
        }


        public void setDetailUrl(String detailUrl) {
            this.detailUrl = detailUrl;
        }


        public String getImages() {
            return images;
        }


        public void setImages(String images) {
            this.images = images;
        }


        public int getNum() {
            return num;
        }


        public void setNum(int num) {
            this.num = num;
        }


        public int getPid() {
            return pid;
        }


        public void setPid(int pid) {
            this.pid = pid;
        }


        public double getPrice() {
            return price;
        }


        public void setPrice(double price) {
            this.price = price;
        }


        public int getPscid() {
            return pscid;
        }


        public void setPscid(int pscid) {
            this.pscid = pscid;
        }


        public int getSelected() {
            return selected;
        }


        public void setSelected(int selected) {
            this.selected = selected;
        }


        public int getSellerid() {
            return sellerid;
        }


        public void setSellerid(int sellerid) {
            this.sellerid = sellerid;
        }


        public String getSubhead() {
            return subhead;
        }


        public void setSubhead(String subhead) {
            this.subhead = subhead;
        }


        public String getTitle() {
            return title;
        }


        public void setTitle(String title) {
            this.title = title;
        }
    }

}

Model实现类

public class Model {
    private Disposable disposable;
    private IPresenter presenter;


    public Model(IPresenter presenter) {
        this.presenter = presenter;
    }


    public void getDataFromNet(String url, Map<String, String> map) {
        if(map == null){
            map = new HashMap<>();
        }
        map.put("source","android");
        RetrofitUtil.getApiService(ApiConfig.baseUrl)
                .doGet(url,map)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                    }


                    @Override
                    public void onNext(ResponseBody responseBody) {
                        try {
                            String json = responseBody.string();
                            presenter.succeed(json);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }


                    @Override
                    public void onError(Throwable e) {
                        presenter.error(e);
                    }


                    @Override
                    public void onComplete() {


                    }
                });
    }


    public void unsubscribe(){
        disposable.dispose();
    }

}

presenter层

public abstract class BasePresenter<T> {
    private Reference<T> mView;


    protected void connect(T view){
         mView = new WeakReference<>(view);
    }


    protected boolean isConnect(){
        return mView!=null && mView.get()!=null;
    }


    protected void removeConnect(){
        if(isConnect()){
            mView.clear();
            mView = null;
        }
    }

}

public interface IPresenter {
    void getDataFromNet(String url, Map<String,String> map);
    void succeed(String json);
    void error(Throwable throwable);
    void destroy();

}

public class PresenterImpl extends BasePresenter implements IPresenter {
    private Model model;
    private IView iView;


    public PresenterImpl(IView iView) {
        model = new Model(this);
        this.iView = iView;
    }


    @Override
    public void getDataFromNet(String url, Map<String, String> map) {
        model.getDataFromNet(url,map);
    }


    @Override
    public void succeed(String json) {
        iView.succeed(json);
    }


    @Override
    public void error(Throwable throwable) {
        iView.error(throwable);
    }


    @Override
    public void destroy() {
        model.unsubscribe();
    }
}

发布了53 篇原创文章 · 获赞 3 · 访问量 7541
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览