Android图表控件MPAndroidChart——BarChart实现多列柱状图和LineChart多曲线 (完结)

首先才接触Android,目前自学一个月,花了一星期,做出了柱状图和曲线图,踩过坑也不少,上代码(主要提供思路,大部分代码可直接用)。

参考代码地址:①曲线:https://blog.csdn.net/ww897532167/article/details/74129478

         ②柱状图:https://blog.csdn.net/ww897532167/article/details/74171294

         ③MPAndroidChart 源码,Github 地址:https://github.com/PhilJay/MPAndroidChart

效果图:

 

柱状图、曲线图。主要代码XML布局

<LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical">
                <com.github.mikephil.charting.charts.BarChart
                    android:id="@+id/barChart"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_marginBottom="20dp" />

            </LinearLayout>




  <LinearLayout
                android:id="@+id/ll_Routine_submit"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical">

                <com.github.mikephil.charting.charts.LineChart
                    android:id="@+id/lineChart"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_marginBottom="20dp" />
            </LinearLayout>
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginTop="5dp"
                android:layout_marginRight="15dp"
                android:layout_marginBottom="15dp">

                <TextView
                    android:id="@+id/tv_chart_so2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="5dp"
                    android:layout_toLeftOf="@+id/tv_chart_NOX"
                    android:drawableLeft="@mipmap/chart_cvt_icon"
                    android:drawablePadding="1dp"
                    android:text="S0₂(t)"
                    android:textColor="@color/black"
                    android:textSize="11dp" />

                <TextView
                    android:id="@+id/tv_chart_NOX"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="5dp"
                    android:layout_toLeftOf="@+id/tv_chart_klw"
                    android:drawableLeft="@mipmap/his_line_2"
                    android:drawablePadding="1dp"
                    android:text="NOX(t)"
                    android:textColor="@color/black"
                    android:textSize="11dp" />

                <TextView
                    android:id="@+id/tv_chart_klw"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="5dp"
                    android:layout_toLeftOf="@+id/tv_chart_fq"
                    android:drawableLeft="@mipmap/chart_val_icon"
                    android:drawablePadding="1dp"
                    android:text="颗粒物(t)"
                    android:textColor="@color/black"
                    android:textSize="11dp" />

                <TextView
                    android:id="@+id/tv_chart_fq"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_marginRight="10dp"
                    android:drawableLeft="@mipmap/his_line_6"
                    android:drawablePadding="1dp"
                    android:text="废气(亿标m³)"
                    android:textColor="@color/black"
                    android:textSize="11dp" />
            </RelativeLayout>

 

 

一、声明变量。

public class GK_QSTab_Fragment extends Fragment implements View.OnClickListener, OnChartValueSelectedListener {
    private View mView;
    private TextView entnum_yc, scsss_ty, zwsss_ty, pks_cb, zwbj, pwbj;
    private Button qs_7sc, qs_7zl, qs_7fq, qs_7fs;
    private gkonlineModel model = null;
    private List<gkonlineModel> jkModelList = null;
    private Type type = new TypeToken<List<gkonlineModel>>() {
    }.getType();

    private BarChart barChart;
    private LineChart lineChart;
    private XAxis xAxisbar, xAxis;                //X轴
    private YAxis leftAxisbar, leftYAxis;            //左侧Y轴
    private YAxis rightAxisbar, rightYaxis;           //右侧Y轴
    private Legend legendbar, legend;              //图例
    private LimitLine limitLine;        //限制线
    //  private MyMarkerView markerView;    //标记视图 即点击xy轴交点时弹出展示信息的View 需自定义
    private int[] colors = new int[]{
            R.color.blue, R.color.cs, R.color.ls, R.color.his_line_color6
    };
}

二、获取数据,调用图表方法。josn数据可自己去获取。

    private class CallBackListener implements DoHttpRequest.CallbackListener {
        @Override
        public void callBack(String result) {
//            if (realtimeList_all != null && realtimeList_all.size() > 0) {
            entnum_yc.setText(model.QYYCSumCount);
            scsss_ty.setText(model.SCTYSumCount);
            zwsss_ty.setText(model.ZWTYSumCount);
            pks_cb.setText(model.PKCBSumCount);

            zwbj.setText(model.ZWSumCount);
            pwbj.setText(model.PWSumCount);
            initBarChart(barChart);
            initChart(lineChart);
            String json = "";
            Gson gson = new Gson();
            //把json序列化为List对象
            List<IncomeBean> list = gson.fromJson(json, new TypeToken<List<IncomeBean>>() {
            }.getType());
            ArrayList<ILineDataSet> dataSets = new ArrayList<>();
            for (int i = 0; i < 4; i++) {
                //加上渐变色+线条颜色
                LineDataSet set = showLineChart(list, getResources().getColor(colors[i]), i);
                dataSets.add(set);
                //                Drawable drawable = getResources().getDrawable(R.drawable.fade_blue);
//                setChartFillDrawable(drawable);
            }
            LineData data = new LineData(dataSets);
            lineChart.setData(data);
            xAxis.setValueFormatter(new IAxisValueFormatter() {
                @Override
                public String getFormattedValue(float value, AxisBase axis) {
                    String tradeDate = list.get((int) value % list.size()).getlEx();
                    return GK_QSTab_Fragment.DateUtil.formatDate(tradeDate);//获取日期
                }
            });
            xAxis.setLabelCount(6, false);//展示X数量
            leftYAxis.setLabelCount(5);//展示Y数量
            //选中 展示数据
            setMarkerView();

            //一周设施停运情况
            String barjosn = "";
//把json序列化为List对象
            List<IncomeBean> listbar = gson.fromJson(barjosn, new TypeToken<List<IncomeBean>>() {
            }.getType());
            ArrayList<IBarDataSet> dataSetsbar = new ArrayList<>();
            List<String> xValues = new ArrayList<>();
            for (IncomeBean valueBean : listbar) {
                xValues.add(valueBean.getlEx());
            }
            for (int i = 0; i < 2; i++) {
                //加上渐变色+线条颜色
                BarDataSet set = showBarChart(xValues,listbar, Name[i], getResources().getColor(colors[i]), i);
                dataSetsbar.add(set);
            }
            BarData bardata = new BarData(dataSetsbar);
            int barAmount = 2; //需要显示柱状图的类别 数量
            //设置组间距占比30% 每条柱状图宽度占比 70% /barAmount  柱状图间距占比 0%
            float groupSpace = 0.3f; //柱状图组之间的间距
            float barWidth = (1f - groupSpace) / barAmount;
            float barSpace = 0f;
            //设置柱状图宽度
            bardata.setBarWidth(barWidth);
            //(起始点、柱状图组间距、柱状图之间间距)
            bardata.groupBars(0f, groupSpace, barSpace);
            barChart.setData(bardata);
            xAxisbar.setAxisMaximum(7);//X 数量
            leftAxisbar.setLabelCount(5);//展示Y数量
//                setAdapter();
//            } else {
//                Toast.makeText(getActivity(), "数据请求失败", Toast.LENGTH_SHORT).show();
//            }
            if (getActivity() != null) {
                getActivity().removeDialog(UIEventListener.UI_EVENT_LOAD_DATA);
            }
        }
    }

三、调用对应样式和方法。

    private class Operating implements DoHttpRequest.OperatingDataListener {
        @Override
        public void callOperatingData(String result) throws ParseException, JSONException {
            if (result != null && !result.equals("")) {
                String json = "";
                Gson gson = new Gson();
                model = gson.fromJson(json, gkonlineModel.class);
//                model = gson.fromJson(result, gkonlineModel.class);
            }
        }
    }

    private void setAdapter() {
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_qs_7fq:
                initData();//请求数据
                break;
            case R.id.btn_qs_7fs:
                break;
        }
    }

    /**
     * 初始化BarChart图表
     */
    private void initBarChart(BarChart barChart) {
        /***图表设置***/
        Description description = new Description();
//        description.setText("需要展示的内容");
        description.setEnabled(false);
        barChart.setDescription(description);
        //背景颜色
        barChart.setBackgroundColor(Color.WHITE);
        //不显示图表网格
        barChart.setDrawGridBackground(false);
        //背景阴影
        barChart.setDrawBarShadow(false);
        barChart.setHighlightFullBarEnabled(false);
        //显示边框
        barChart.setDrawBorders(false);
        //设置动画效果
        barChart.animateY(1000);
        barChart.animateX(1000);
        barChart.setScaleEnabled(false); //是否支持缩放,默认true
        /***XY轴的设置***/
        //X轴设置显示位置在底部

        xAxisbar = barChart.getXAxis();
        xAxisbar.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxisbar.setAxisMinimum(0f);

//        xAxisbar.setAxisMaximum(6);
        //将X轴的值显示在中央
        xAxisbar.setCenterAxisLabels(true);
        xAxisbar.setGranularity(1f);


        leftAxisbar = barChart.getAxisLeft();
        rightAxisbar = barChart.getAxisRight();
        //保证Y轴从0开始,不然会上移一点
        leftAxisbar.setAxisMinimum(0f);
        rightAxisbar.setAxisMinimum(0f);
        //不显示X轴网格线
        xAxisbar.setDrawGridLines(false);
        //右侧Y轴网格线设置为虚线
        rightAxisbar.enableGridDashedLine(10f, 10f, 0f);
        rightAxisbar.setEnabled(false);//去掉右侧Y轴
        /***图例 标签 设置***/
        legendbar = barChart.getLegend();
        legendbar.setForm(Legend.LegendForm.SQUARE);
        legendbar.setTextSize(11f);
        //显示位置
        legendbar.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
        legendbar.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
        legendbar.setOrientation(Legend.LegendOrientation.HORIZONTAL);
        //是否绘制在图表里面
        legendbar.setDrawInside(false);
    }

    /**
     * 柱状图始化设置 一个BarDataSet 代表一列柱状图
     *
     * @param barDataSet 柱状图
     * @param color      柱状图颜色
     */
    private void initBarDataSet(BarDataSet barDataSet, int color) {
        barDataSet.setColor(color);
        barDataSet.setFormLineWidth(1f);
        barDataSet.setFormSize(15.f);
        barDataSet.setDrawValues(true);//柱状图顶部值
//        barDataSet.setValueTextSize(10f);
//        barDataSet.setValueTextColor(color);
    }

    public BarDataSet showBarChart(List<String> xValues, List<IncomeBean> dataList, String name, int color, int ii) {
        List<BarEntry> entries = new ArrayList<>();
        for (int i = 0; i < dataList.size(); i++) {
            IncomeBean data = dataList.get(i);
            switch (ii) {
                case 0:
                    BarEntry entry = new BarEntry(i, (float) data.getlFQNUM());
                    entries.add(entry);
                    break;
                case 1:
                    BarEntry entry1 = new BarEntry(i, (float) data.getlFSNUM());
                    entries.add(entry1);
                    break;
            }

        }
        //X轴自定义值
        xAxisbar.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                String tradeDate= xValues.get((int) Math.abs(value) % xValues.size());
                return GK_QSTab_Fragment.DateUtil.formatDatebar(tradeDate);//获取日期
            }
        });
        // 每一个BarDataSet代表一类柱状图
        BarDataSet barDataSet = new BarDataSet(entries, name);
        initBarDataSet(barDataSet, color);
        return barDataSet;
    }

    /**
     * 初始化图表
     */
    private void initChart(LineChart lineChart) {
        /***图表设置***/
        //右下角还有一个描述标签 Descripition Lable
        Description description = new Description();
//        description.setText("需要展示的内容");
        description.setEnabled(false);
        lineChart.setDescription(description);

        //是否展示网格线
        lineChart.setDrawGridBackground(false);
        //是否显示边界
        lineChart.setDrawBorders(true);
        //是否可以拖动
        lineChart.setDragEnabled(false);
        //是否有触摸事件
        lineChart.setTouchEnabled(true);
        //设置XY轴动画效果
        lineChart.animateY(2500);
        lineChart.animateX(1500);

        /***XY轴的设置***/
        lineChart.setDrawBorders(false);//是否显示边界
        lineChart.getXAxis().setDrawGridLines(false);
        lineChart.getAxisRight().setDrawGridLines(false);
        lineChart.getAxisLeft().setDrawGridLines(true);//X Y轴网格线
        lineChart.getAxisLeft().enableGridDashedLine(10f, 10f, 0f);//设置X Y轴网格线为虚线(实体线长度、间隔距离、偏移量:通常使用 0)
        lineChart.getAxisRight().setEnabled(false);//去掉右侧Y轴

        xAxis = lineChart.getXAxis();
        leftYAxis = lineChart.getAxisLeft();
        rightYaxis = lineChart.getAxisRight();
        //X轴设置显示位置在底部
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
        xAxis.setAxisMinimum(0f);
        xAxis.setGranularity(1f);
        //保证Y轴从0开始,不然会上移一点
        leftYAxis.setAxisMinimum(0f);
        rightYaxis.setAxisMinimum(0f);

        /***折线图例 标签 设置***/
        legend = lineChart.getLegend();
        //设置显示类型,LINE CIRCLE SQUARE EMPTY 等等 多种方式,查看LegendForm 即可
        legend.setForm(Legend.LegendForm.LINE);
        legend.setTextSize(12f);
        //显示位置 左下方
        legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
        legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
        legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
        //是否绘制在图表里面
        legend.setDrawInside(false);
        legend.setEnabled(false);//隐藏图列
    }

    /**
     * 曲线初始化设置 一个LineDataSet 代表一条曲线
     *
     * @param lineDataSet 线条
     * @param color       线条颜色
     * @param mode
     */
    private void initLineDataSet(LineDataSet lineDataSet, int color, LineDataSet.Mode mode) {
        lineDataSet.setColor(color);
        lineDataSet.setCircleColor(color);
        lineDataSet.setLineWidth(1.5f);
        lineDataSet.setCircleRadius(3f);
        lineDataSet.setDrawCircles(false);//*不显示点
        lineDataSet.setDrawValues(false);//*不显示值
        //显示值并对值 进行修改。
//        lineDataSet.setValueFormatter(new IValueFormatter() {
//            @Override
//            public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
//                DecimalFormat df = new DecimalFormat(".00");
//                return df.format(value * 100) + "%";
//            }
//        });
        //设置曲线值的圆点是实心还是空心
        lineDataSet.setDrawCircleHole(false);
        lineDataSet.setValueTextSize(10f);
        //设置折线图填充
        lineDataSet.setDrawFilled(false);//线条背景色
        lineDataSet.setFormLineWidth(1f);
        lineDataSet.setFormSize(15.f);
        if (mode == null) {
            //设置曲线展示为圆滑曲线(如果不设置则默认折线)
            lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
        } else {
            lineDataSet.setMode(mode);
        }
    }

    /**
     * 展示曲线
     *
     * @param dataList 数据集合
     * @param color    曲线颜色
     */
    public LineDataSet showLineChart(List<IncomeBean> dataList, int color, int ii) {
        List<Entry> entries = new ArrayList<>();
        for (int i = 0; i < dataList.size(); i++) {
            IncomeBean data = dataList.get(i);
            Entry entry = new Entry();
            switch (ii) {
                case 0:
                    entry = new Entry(i, (float) data.getlSO2PFL());//获取曲线值
                    break;
                case 1:
                    entry = new Entry(i, (float) data.getlNOXPFL());//获取曲线值
                    break;
                case 2:
                    entry = new Entry(i, (float) data.getlDUSTPFL());//获取曲线值
                    break;
                case 3:
                    entry = new Entry(i, (float) data.getlFQPFL() / 10000);//获取曲线值
                    break;
            }
            entries.add(entry);
        }
        // 每一个LineDataSet代表一条线
//        LineDataSet d = new LineDataSet(entries, name);
        LineDataSet d = new LineDataSet(entries, "");
        initLineDataSet(d, color, LineDataSet.Mode.LINEAR);
        return d;
    }

    /**
     * 数据获取到的时间为 20180502 我们需要显示 05-02 所以需要进行日期转换
     * 一个日期转换工具
     */
    public static class DateUtil {

        public static String formatDate(String str) {
            SimpleDateFormat sf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            SimpleDateFormat sf2 = new SimpleDateFormat("dd日HH时");
            String formatStr = "";
            try {
                formatStr = sf2.format(sf1.parse(str));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return formatStr;
        }

        public static String formatDatebar(String str) {
            SimpleDateFormat sf1 = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sf2 = new SimpleDateFormat("MM-dd");
            String formatStr = "";
            try {
                formatStr = sf2.format(sf1.parse(str));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return formatStr;
        }
    }

    /**
     * 设置线条填充背景颜色
     *
     * @param drawable
     */
    public void setChartFillDrawable(Drawable drawable) {
        if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) {
            LineDataSet lineDataSet = (LineDataSet) lineChart.getData().getDataSetByIndex(0);
            //避免在 initLineDataSet()方法中 设置了 lineDataSet.setDrawFilled(false); 而无法实现效果
            lineDataSet.setDrawFilled(true);
            lineDataSet.setFillDrawable(drawable);
            lineChart.invalidate();
        }
    }

    /**
     * 设置 可以显示X Y 轴自定义值的 MarkerView
     */
    public void setMarkerView() {
        LineChartMarkView mv = new LineChartMarkView(this.getActivity(), xAxis.getValueFormatter());
        mv.setChartView(lineChart);
        lineChart.setMarker(mv);
        lineChart.invalidate();
    }
    private final RectF onValueSelectedRectF = new RectF();
    //柱状图 点击明细
    public void onValueSelected(Entry e, Highlight h) {

        if (e == null)
            return;

        RectF bounds = onValueSelectedRectF;
        barChart.getBarBounds((BarEntry) e, bounds);
        MPPointF position = barChart.getPosition(e, YAxis.AxisDependency.LEFT);

        Log.i("bounds", bounds.toString());
        Log.i("position", position.toString());

        Log.i("x-index",
                "low: " + barChart.getLowestVisibleX() + ", high: "
                        + barChart.getHighestVisibleX());

        MPPointF.recycleInstance(position);
    }

    @Override
    public void onNothingSelected() {

    }
/*
 * 曲线点击图表查看 明细。主要注意 上标和下标,必须通过Textview 
 * */
public class LineChartMarkView extends MarkerView {

    private TextView tvDate,tvValue0,tvValue1,tvValue2,tvValue3,chart_so2;
    private IAxisValueFormatter xAxisValueFormatter;
    DecimalFormat df = new DecimalFormat(".00");

    public LineChartMarkView(Context context, IAxisValueFormatter xAxisValueFormatter) {
        super(context, R.layout.layout_markview);
        this.xAxisValueFormatter = xAxisValueFormatter;

        tvDate = findViewById(R.id.tv_date1);
        tvValue0 = findViewById(R.id.tv_value0);
        tvValue1 = findViewById(R.id.tv_value1);
        tvValue2 = findViewById(R.id.tv_value2);
        tvValue3 = findViewById(R.id.tv_value3);
    }

//    @SuppressLint("SetTextI18n")
    @Override
    public void refreshContent(Entry e, Highlight highlight) {
        //角标上标
        SpannableString m3 = new SpannableString("废气(亿标m3):");
        m3.setSpan(new RelativeSizeSpan(0.5f), 6, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        m3.setSpan(new SuperscriptSpan(), 6, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);   //上标
        SpannableStringBuilder fqBuilder = new SpannableStringBuilder();
        fqBuilder.append(m3);
        //角标下标
        SpannableString B = new SpannableString("SO2(t):");
        B.setSpan(new RelativeSizeSpan(0.5f), 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);//一半大小
        B.setSpan(new SubscriptSpan(), 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);   //上标
        SpannableStringBuilder SO2Builder = new SpannableStringBuilder();
        SO2Builder.append(B);

        Chart chart = getChartView();
        if (chart instanceof LineChart) {
            LineData lineData = ((LineChart) chart).getLineData();

            List<ILineDataSet> dataSetList = lineData.getDataSets();
            for (int i = 0; i < dataSetList.size(); i++) {
                LineDataSet dataSet = (LineDataSet) dataSetList.get(i);
                float y = dataSet.getValues().get((int) e.getX()).getY();
                switch (i){
                    case 0:
                        SO2Builder.append(Float.toString(y));
                        tvValue0.setText(SO2Builder);
                        break;
                    case 1:
                        tvValue1.setText("NOX(t):" + "" + y);
                        break;
                    case 2:
                        tvValue2.setText("颗粒物(t):" + "" + y);
                        break;
                    case 3:
                        fqBuilder.append(Float.toString(y));
                        tvValue3.setText(fqBuilder);
                        break;
                }
            }
            tvDate.setText(xAxisValueFormatter.getFormattedValue(e.getX(), null));
        }
        //???????X??? ???X??????
//        tvDate.setText(xAxisValueFormatter.getFormattedValue(e.getX(), null));
//        tvValue.setText("SO2(t)??" + e.getY());
        super.refreshContent(e, highlight);
    }

    @Override
    public MPPointF getOffset() {
        return new MPPointF(-(getWidth() / 2), -getHeight());
    }
}

曲线图点击x y 对应值的页面

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

    <TextView
        android:id="@+id/tv_date1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white" />

    <TextView
        android:id="@+id/tv_value0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:textColor="@android:color/white" />
    <TextView
        android:id="@+id/tv_value1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:textColor="@android:color/white" />
    <TextView
        android:id="@+id/tv_value2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:textColor="@android:color/white" />
    <TextView
        android:id="@+id/tv_value3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:textColor="@android:color/white" />

</LinearLayout>

 

posted @ 2022-08-11 10:51  羁绊lov  阅读(3789)  评论(0编辑  收藏  举报