Android 视图中的复杂画布查看滚动条

Android 视图中的复杂画布查看滚动条,android,canvas,android-recyclerview,Android,Canvas,Android Recyclerview,下面是我们用来绘制天气图的代码,它显示为recyclerview中的视图。接近此视图时,列表滚动在绘制画布时向下移动 有人能提出一个解决问题的办法吗?也许是一种在背景中画画的方法,还是一种更好的画法 public class WeatherHourlyForecastGraphView extends View { private static String LOG_TAG = WeatherHourlyForecastGraphView.class.getSimpleName();

下面是我们用来绘制天气图的代码,它显示为recyclerview中的视图。接近此视图时,列表滚动在绘制画布时向下移动

有人能提出一个解决问题的办法吗?也许是一种在背景中画画的方法,还是一种更好的画法

public class WeatherHourlyForecastGraphView extends View {
    private static String LOG_TAG = WeatherHourlyForecastGraphView.class.getSimpleName();

    private static int TEMPERATURE = 0;
    private static int PRECIPITATION = 1;
    private static final String TIME_FORMAT = "h a";

    // public static final int GRAPH_HEIGHT_PHONE = 145;
    // public static final int GRAPH_HEIGHT_TABLET = 188;

    public static final int GRAPH_TOP = (DeviceInfo.isDeviceAPhone() ? 145 : 186);
    public static final int GRAPH_DISPLAY_HEIGHT = GRAPH_TOP - 9;  // 100 will be displayed at GRAPH_DISPLAY_HEIGHT

    private Rect mTextBounds;

    // Handle hour in graph being tapped
    public interface OnHourSelectionListener {
        void onHourSelected(int selectedPosition);
    }

    // Temperature and precipitation values
    private ArrayList<Double> mPrecipitationValues = new ArrayList<>();
    private ArrayList<Double> mTemperatureValues = new ArrayList<>();

    // Current city for weather - retrieve hourly for this city
    private City mCurrentCityWeather;

    // Icons
    private HashMap<String, Bitmap> mWeatherConditionIcons = new HashMap<>();
    private Bitmap mPrecipitationIcon;

    // Paint
    private Paint mTemperaturePaint;
    private Paint mPrecipitationPaint;
    private Paint mPrecipitationLinePaint;
    private Paint mGraphPaperLinePaint;
    private Paint mDotPaint;
    private Paint mWhitePaint;
    private Paint mTimeTextPaint;
    private Paint mTemperatureTextPaint;
    private Paint mPrecipitationTextPaint;
    private Paint mSelectedPaint;

    // When hour is selected. Redraw graph, update conditions
    private int mSelectedPosition;
    private OnHourSelectionListener mOnHourSelectionListener;
    private GestureDetector.SimpleOnGestureListener gestureListener = new GestureListener();
    private final GestureDetector gestureDetector = new GestureDetector(getContext(), gestureListener);

    // Width (x -values) for displaying single hour in graph
    private int mHalfHourlyDisplayWidth;
    private int mHourlyDisplayWidth;

    private int timeTopPadding;
    private int iconTopPadding;
    private int tempTopPadding;
    private int precipTopPadding;
    private int mGraphHeightWithPadding;
    private int mContentHeight;
    private RectF mIconRectF;
    private int numHours = 24;
    private Paint backgroundPaint;
    private Path tempGeneratePath;

    private MyNews myNews;
    ArrayList<Double> pathValues;

    public WeatherHourlyForecastGraphView(Context context) {
        super(context);
    }

    public WeatherHourlyForecastGraphView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WeatherHourlyForecastGraphView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public WeatherHourlyForecastGraphView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    {
        mIconRectF = new RectF();
        backgroundPaint = new Paint();
        tempGeneratePath = new Path();
        myNews = MyNews.getMyNews();
        pathValues = new ArrayList<>();

        initializePaintsInUse();
        initializeDimensions();
        mPrecipitationIcon = BitmapFactory.decodeResource(getResources(), R.drawable.rain_drop);

        mTextBounds = new Rect(); // Used to calculate the text bounds while drawing. Initialize in constructor to avoid creating object in onDraw()
        // setLayerType(LAYER_TYPE_SOFTWARE, null);
    }

    private void initializePaintsInUse() {
        mTemperaturePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTemperaturePaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_temperature_color));
        mTemperaturePaint.setStrokeWidth(3);
        mTemperaturePaint.setStyle(Paint.Style.STROKE);

        mPrecipitationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPrecipitationPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_precip_color));
        mPrecipitationPaint.setStrokeWidth(3);

        mPrecipitationLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPrecipitationLinePaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_precip_line_color));
        mPrecipitationLinePaint.setStrokeWidth(3);
        mPrecipitationLinePaint.setStyle(Paint.Style.STROKE);

        mGraphPaperLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mGraphPaperLinePaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_paper_line_color));
        mGraphPaperLinePaint.setStrokeWidth(1);
        mGraphPaperLinePaint.setPathEffect(null);
        mGraphPaperLinePaint.setStyle(Paint.Style.STROKE);

        mWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mWhitePaint.setColor(Color.WHITE);
        mWhitePaint.setStrokeWidth(DeviceInfo.getValuesInPixel(1));

        mSelectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSelectedPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_selected_item_color));
        mSelectedPaint.setStrokeWidth(2);

        mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDotPaint.setColor(ContextCompat.getColor(getContext(), R.color.pale_blue));
        mDotPaint.setStrokeWidth(2);

        mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_time_text_color));
        mTimeTextPaint.setTextSize(getResources().getDimension(R.dimen.hourly_forecast_graph_time_text_size));
        mTimeTextPaint.setTypeface(FontManager.getInstance().get(getResources().getString(R.string.roboto_medium_font), Typeface.NORMAL));

        mTemperatureTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTemperatureTextPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_temperature_text_color));
        mTemperatureTextPaint.setTextSize(getResources().getDimension(R.dimen.hourly_forecast_graph_temperature_text_size));
        mTemperatureTextPaint.setTypeface(DeviceInfo.isDeviceAPhone() ? Typeface.create(getResources().getString(R.string.roboto), Typeface.NORMAL)
                : FontManager.getInstance().get(getResources().getString(R.string.roboto_medium_font), Typeface.NORMAL));

        mPrecipitationTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPrecipitationTextPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_temperature_precipitation_color));
        mPrecipitationTextPaint.setTextSize(getResources().getDimension(R.dimen.hourly_forecast_graph_precipitation_text_size));
        mPrecipitationTextPaint.setTypeface(Typeface.create(getResources().getString(R.string.roboto), Typeface.NORMAL));  // was roboto light
    }

    private void initializeDimensions() {
        // Width when displaying weather for single hour
        mHourlyDisplayWidth = DeviceInfo.getValuesInPixel(DeviceInfo.isDeviceAPhone() ? 65 : 101);  // was 55, 85
        mHalfHourlyDisplayWidth = mHourlyDisplayWidth / 2;
        // mGraphHeightWithPadding = DeviceInfo.getValuesInPixel(DeviceInfo.isDeviceAPhone() ? 145 : 188);
        mGraphHeightWithPadding = DeviceInfo.getValuesInPixel(GRAPH_TOP);

        mContentHeight = (int) getResources().getDimension(R.dimen.hourly_forecast_content_height);
        timeTopPadding = (int) getResources().getDimension(R.dimen.hourly_forecast_content_time_top_padding);
        iconTopPadding = (int) getResources().getDimension(R.dimen.hourly_forecast_content_icon_top_padding);
        tempTopPadding = (int) getResources().getDimension(R.dimen.hourly_forecast_content_temperature_top_padding);
        precipTopPadding = (int) getResources().getDimension(R.dimen.hourly_forecast_content_precipitation_top_padding);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(MeasureSpec.getSize(numHours * mHourlyDisplayWidth), MeasureSpec.getSize(mGraphHeightWithPadding + mContentHeight));
    }

    /**
     * This view's onDraw method
     * Uses 2 bitmaps to paint each half of graph (Hardware LayerType supports only particular length)
     * That avoids issue of path disappearing
     * See issues related to canvas rescaling here: https://developer.android.com/guide/topics/graphics/hardware-accel.html
     *
     * @param canvas the canvas to draw
     */
    @Override
    protected void onDraw(Canvas canvas) {
        // FIXME: Move object allocations out of onDraw(). Also look for object allocations in the methods called by onDraw() as well
        // Create left bitmap
        Bitmap bitmapLeft = Bitmap.createBitmap(numHours / 2 * mHourlyDisplayWidth, canvas.getHeight(), Bitmap.Config.ARGB_8888);
        bitmapLeft.setDensity(DisplayMetrics.DENSITY_DEFAULT);
        Canvas bitmapCanvasLeft = new Canvas(bitmapLeft);
        bitmapCanvasLeft.drawColor(Color.WHITE);
        drawGraphBackground(bitmapCanvasLeft);

        // Create right bitmap
        Bitmap bitmapRight = Bitmap.createBitmap(numHours / 2 * mHourlyDisplayWidth, canvas.getHeight(), Bitmap.Config.ARGB_8888);
        bitmapLeft.setDensity(DisplayMetrics.DENSITY_DEFAULT);
        Canvas bitmapCanvasRight = new Canvas(bitmapRight);
        bitmapCanvasRight.drawColor(Color.WHITE);
        drawGraphBackground(bitmapCanvasRight);

        // Draw Temperature graph into each bitmap
        drawTemperatureGraph(bitmapCanvasLeft, 0);  // always call left side first
        drawTemperatureGraph(bitmapCanvasRight, 12);

        // Draw Precipitation graph into each bitmap
        drawPrecipitationGraph(bitmapCanvasLeft, 0);  // always call left side first
        drawPrecipitationGraph(bitmapCanvasRight, 12);

        // Draw bitmaps into canvas
        canvas.drawBitmap(bitmapLeft, 0f, 0f, null);
        canvas.drawBitmap(bitmapRight, numHours / 2 * mHourlyDisplayWidth, 0f, null);

        // Highlight selected hour
        drawSelectedHour(canvas);

        // Draw Graph lines
        drawGraphLines(canvas);

        // Draw text under graph
        drawText(canvas);
    }

    /**
     * Draw background including top/bottom colors and graph lines
     *
     * @param canvas the canvas to draw
     */
    private void drawGraphBackground(Canvas canvas) {
        backgroundPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_background_color));
        canvas.drawRect(0, 0, canvas.getWidth(), mGraphHeightWithPadding, backgroundPaint);
    }

    private void drawTemperatureGraph(Canvas canvas, int start) {
        Path temperaturePath = generatePath(TEMPERATURE, start);
        canvas.drawPath(temperaturePath, mTemperaturePaint);
    }

    private void drawPrecipitationGraph(Canvas canvas, int start) {
        Path precipPath = generatePath(PRECIPITATION, start);
        // draw filled
        canvas.drawPath(precipPath, mPrecipitationPaint);
        // draw line around all
        canvas.drawPath(precipPath, mPrecipitationLinePaint);
        // hide bottom line
        canvas.drawLine(-5, mGraphHeightWithPadding, numHours * mHourlyDisplayWidth, mGraphHeightWithPadding, mPrecipitationPaint);
    }

    /**
     * Given a Temperature or Precipitation value to display, return Y value on graph
     *
     * @param valueToDisplay the graph value to be displayed
     * @return the y position on the graph
     */
    private float getGraphHeightForValue(double valueToDisplay) {
        // float topGraphValue = (float) (DeviceInfo.isDeviceAPhone() ? 145 * pixelHeight : 186 * pixelHeight);  // handle -2 to 112,  Temperature 0 is displayed 2 above bottom of graph
        // float displayHeight = (float) (DeviceInfo.isDeviceAPhone() ? 140 * pixelHeight: 181 * pixelHeight);  // handle -2 to 112,  Temperature 0 is displayed 2 above bottom of graph

        float topGraphValue = DeviceInfo.getValuesInPixel(GRAPH_TOP);  // handle -2 to 112,  Temperature 0 is displayed 2 above bottom of graph
        float displayHeight = DeviceInfo.getValuesInPixel(GRAPH_DISPLAY_HEIGHT);  // handle -2 to 112,  Temperature 0 is displayed 2 above bottom of graph

        float ratio = (float) (valueToDisplay / 100);  // 78 degrees = 78%,  103 degrees = 103%
        float y = ratio * displayHeight; // number of pixes out of 107);
        return topGraphValue - y;
    }

    /**
     * The selected hour is displayed as rectangle, vertical line through selected value, and circle around hour
     *
     * @param canvas the canvas to draw
     */
    private void drawSelectedHour(Canvas canvas) {
        // double selectedTemperature = mTemperatureValues.get(mSelectedPosition);
        float selectedX = mSelectedPosition * mHourlyDisplayWidth + mHalfHourlyDisplayWidth;

        // Draw line on graph
        mTemperaturePaint.setStrokeWidth(1);
        canvas.drawLine(selectedX, 0, selectedX, mGraphHeightWithPadding, mTemperaturePaint);

        mTemperaturePaint.setStrokeWidth(3);

        // Draw Rectangle on graph and text
        mSelectedPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_selected_item_color));
        // canvas.drawRect(selectedX-mHalfHourlyDisplayWidth, 0, selectedX+mHalfHourlyDisplayWidth, mGraphHeightWithPadding, mSelectedPaint );
        canvas.drawRect(selectedX - mHalfHourlyDisplayWidth, 0, selectedX + mHalfHourlyDisplayWidth, canvas.getHeight(), mSelectedPaint);

        // Draw Circles
        drawSelectedCircles(canvas);
    }

    private void drawGraphLines(Canvas canvas) {
        // draw horizontal lines at 0, 25,75, 100
        for (double i = 0; i <= 100; i += 25) {
            canvas.drawLine(0, getGraphHeightForValue(i), canvas.getWidth(), getGraphHeightForValue(i), mGraphPaperLinePaint);
        }

        // draw vertical lines in center of each hour unit
        for (int index = 0; index < numHours; index++) {
            float selectedX = index * mHourlyDisplayWidth;
            // float selectedX = index * mHourlyDisplayWidth + mHalfHourlyDisplayWidth;
            canvas.drawLine(selectedX, 0, selectedX, canvas.getHeight(), mGraphPaperLinePaint);
        }

        // redraw circles over graph lines
        drawSelectedCircles(canvas);
    }

    private void drawSelectedCircles(Canvas canvas) {
        double selectedTemperature = mTemperatureValues.get(mSelectedPosition);
        float selectedX = mSelectedPosition * mHourlyDisplayWidth + mHalfHourlyDisplayWidth;
        float selectedY = getGraphHeightForValue(selectedTemperature);
        mDotPaint.setColor(ContextCompat.getColor(getContext(), R.color.hourly_forecast_graph_temperature_color));
        canvas.drawCircle(selectedX, selectedY, getResources().getDimension(R.dimen.hourly_forecast_graph_selected_outer_circle_radius), mDotPaint);
        canvas.drawCircle(selectedX, selectedY, getResources().getDimension(R.dimen.hourly_forecast_graph_selected_middle_circle_radius), mWhitePaint);
        canvas.drawCircle(selectedX, selectedY, getResources().getDimension(R.dimen.hourly_forecast_graph_selected_inner_circle_radius), mDotPaint);
    }

    private void drawText(Canvas canvas) {
        int xAxisValue = mHalfHourlyDisplayWidth;
        float width, height;
        Bitmap bitmap;
        for (Hour hour : mCurrentCityWeather.hourlyForecast) {
            String value = hour.getLocalTime(TIME_FORMAT);

            // Draw Time Value
            width = mTimeTextPaint.measureText(value);
            height = mGraphHeightWithPadding + timeTopPadding + mTimeTextPaint.getTextSize();

            canvas.drawText(value, (xAxisValue - (width / 2)), height, mTimeTextPaint);

            // Draw Weather Conditions Icon
            bitmap = mWeatherConditionIcons.get(myNews.getGlobal().manifest.appBaseUrls.getWeatherConditionIconUrl(hour.iconCode));
            height += iconTopPadding;
            if (bitmap != null) {
                // Set width to 24 for handheld and large.  30 for extra large
                width = DeviceInfo.getValuesInPixel(24);
                if (DeviceInfo.isDeviceATablet()) {
                    String screenSize = DeviceInfo.getScreenType();
                    if (screenSize.equalsIgnoreCase("xlarge")) {
                        width = DeviceInfo.getValuesInPixel(30);
                    }
                }
                // width = DeviceInfo.getValuesInPixel(DeviceInfo.isDeviceAPhone() ? 24 : 30);
                // mIconRectF.set((xAxisValue - (width / 2)), height, (xAxisValue + (width / 2)), height +  DeviceInfo.getValuesInPixel(DeviceInfo.isDeviceAPhone() ? 24 : 30));
                mIconRectF.set((xAxisValue - (width / 2)), height, (xAxisValue + (width / 2)), height + width);
                canvas.drawBitmap(bitmap, null, mIconRectF, null);
            }
            // height += DeviceInfo.getValuesInPixel(24);
            height += width;
            value = StringUtils.appendDegree(hour.getTemperature());
            // Draw Temperature value
            width = mTemperatureTextPaint.measureText(value);
            height += tempTopPadding + mPrecipitationTextPaint.getTextSize();

            canvas.drawText(value, xAxisValue - (width / 2), height, mTemperatureTextPaint);

            value = hour.getRainFallInInt() + "%"; // 10% for example
            int iconWidth = mPrecipitationIcon.getScaledWidth(canvas) + DeviceInfo.getValuesInPixel(2);
            // int iconWidth = DeviceInfo.getValuesInPixel(DeviceInfo.isDeviceAPhone() ? 12 : 18);
            width = iconWidth + mPrecipitationTextPaint.measureText(value);
            height += precipTopPadding;

            // No need to use the rectangle method here because the image is already in perfect size
            canvas.drawBitmap(mPrecipitationIcon, (xAxisValue - (width / 2)), height, null);

            mPrecipitationTextPaint.getTextBounds(value, 0, value.length(), mTextBounds);
            height += (mPrecipitationIcon.getScaledHeight(canvas) - mTextBounds.height()) / 2 + mTextBounds.height();
            canvas.drawText(value, (xAxisValue - (width / 2) + iconWidth), height, mPrecipitationTextPaint);

            xAxisValue += mHourlyDisplayWidth;
        }
    }

    private Path generatePath(int type, int startIndex) {
        pathValues.clear();
        tempGeneratePath.reset();

        if (type == TEMPERATURE) {
            pathValues.addAll(mTemperatureValues);
        } else if (type == PRECIPITATION) {
            pathValues.addAll(mPrecipitationValues);
        }
        tempGeneratePath.setFillType(Path.FillType.EVEN_ODD);

        float x = mHalfHourlyDisplayWidth;
        float y = getGraphHeightForValue(pathValues.get(startIndex));
        float x1 = mHourlyDisplayWidth + mHalfHourlyDisplayWidth; //1
        float y1 = getGraphHeightForValue(pathValues.get(startIndex + 1));
        float xControlPoint = (x + x1) / 2;
        float yControlPoint = (y + y1) / 2;

        if (startIndex == 0) {
            tempGeneratePath.moveTo(-1, y);  // start on edge of graph
            // path.moveTo(0, y);  // start on edge of graph
        } else {
            float lastY = getGraphHeightForValue(pathValues.get(pathValues.size() / 2 - 1));  // last point on left graph
            tempGeneratePath.moveTo(0, lastY);  // start at height of last value in left hand graph
        }
        tempGeneratePath.lineTo(x, y);
        tempGeneratePath.quadTo(xControlPoint, yControlPoint, x1, y1);

        for (int index = 1; index < pathValues.size() / 2 - 1; index++) {
            x = mHourlyDisplayWidth * index + mHalfHourlyDisplayWidth;  // current point
            y = getGraphHeightForValue(pathValues.get(index + startIndex));  // use start to get proper valye

            x1 = mHourlyDisplayWidth * (index + 1) + mHalfHourlyDisplayWidth;  // next point
            y1 = getGraphHeightForValue(pathValues.get(index + startIndex + 1));

            xControlPoint = (x + x1) / 2;  // control point
            yControlPoint = (y + y1) / 2;

            tempGeneratePath.quadTo(xControlPoint, yControlPoint, x1, y1);
        }

        // By going outside of viewable area, we can fill the precipitation path and draw a line on top
        tempGeneratePath.lineTo(x1 + mHourlyDisplayWidth, y1);  // add line at end, that goes past viewable area on left graph
        tempGeneratePath.lineTo(x1 + mHourlyDisplayWidth, mGraphHeightWithPadding); // below viewable area
        tempGeneratePath.lineTo(-5, mGraphHeightWithPadding); //below viewable area, to left of viewable area
        tempGeneratePath.close();

        return tempGeneratePath;
    }

    /**
     * Called in WeatherAdapter
     *
     * @param currentCityWeather      the weather data
     * @param onHourSelectionListener the listener to invoke when an hour is selected on the hourly forecast graph
     */
    public void setCurrentCityWeather(City currentCityWeather, OnHourSelectionListener onHourSelectionListener) {
        if (currentCityWeather == null) {
            return;
        }

        mCurrentCityWeather = currentCityWeather;
        mOnHourSelectionListener = onHourSelectionListener;
        mSelectedPosition = 0;

        ArrayList<Double> temperatureArray = new ArrayList<>();
        ArrayList<Double> precipitationArray = new ArrayList<>();

        if (currentCityWeather.hourlyForecast != null) {
            for (Hour hour : currentCityWeather.hourlyForecast) {
                if (hour != null) {
                    String weatherConditionIconUrl = myNews.getGlobal().manifest.appBaseUrls.getWeatherConditionIconUrl(hour.iconCode);
                    myNews.getVolleyHelper().getImageLoader().get(weatherConditionIconUrl, new ImageLoader.ImageListener() {
                        @Override
                        public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) {
                            mWeatherConditionIcons.put(imageContainer.getRequestUrl(), imageContainer.getBitmap());
                            invalidate(); // Find a better way to draw these images. Do not invalidate the entire view all times
                        }

                        @Override
                        public void onErrorResponse(VolleyError volleyError) {

                        }
                    });
                    temperatureArray.add(hour.getTemperatureInDouble());
                    precipitationArray.add(hour.getRainFallInDouble());
                } else {
                    temperatureArray.add(0.0);
                    precipitationArray.add(0.0);
                }
            }
        }

        // Set Values
        setTemperatureValues(temperatureArray);
        setPrecipitationValues(precipitationArray);

        // Redraw the view
        requestLayout();
        invalidate();
    }

    private void setTemperatureValues(ArrayList<Double> temperatureArray) {
        mTemperatureValues.clear();
        mTemperatureValues.addAll(temperatureArray);
    }

    private void setPrecipitationValues(ArrayList<Double> precipitationArray) {
        mPrecipitationValues.clear();
        mPrecipitationValues.addAll(precipitationArray);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
    }

    /**
     * notifySelectionListener
     * User has selected an hour in the graph
     *
     * @param x the x position of the tap event
     * @param y the y position of the tap event
     * @return {@code true} if the selection is valid, {@code false} otherwise
     */
    private boolean notifySelectionListener(float x, float y) {
        return determineSelectedHour(x, y);
    }

    /**
     * determineSelectedHour
     * Based on coordinates on graph, determine the hour selected
     *
     * @param x the x position of the tap event
     * @param y the y position of the tap event
     * @return {@code true} if the selection is valid, {@code false} otherwise
     */
    private boolean determineSelectedHour(float x, float y) {
        // x is what matters
        // hour will be 0 to 23

        float graphWidth = numHours * mHourlyDisplayWidth;
        float selectedXProportion = x / graphWidth;
        float selectedHour = selectedXProportion * numHours;
        int selectHourInt = (int) selectedHour;

        if (0 <= selectedHour && selectedHour < numHours) {
            mSelectedPosition = selectHourInt;
            if (mOnHourSelectionListener != null) {
                mOnHourSelectionListener.onHourSelected(mSelectedPosition);
                myNews.getOmnitureHelper().sendClickAction(OmnitureConstants.FORECAST_LANDING_HOURLY_FORECAST_SWIPE_ACTION,
                        OmnitureConstants.FORECAST_LANDING_MODULE);
            }
            invalidate();  // this is what causes the redraw
            return true;
        }
        return false;
    }

    class GestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent event) {
            return notifySelectionListener(event.getX(), event.getY());
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return false;
        }
    }
}
公共类WeatherHourlyForecastGraphView扩展视图{
私有静态字符串LOG_TAG=WeatherHourlyForecastGraphView.class.getSimpleName();
专用静态内部温度=0;
私有静态int=1;
私有静态最终字符串时间\u FORMAT=“h a”;
//公共静态最终整数图\高度\电话=145;
//公共静态最终整型图\高度\平板=188;
公共静态final int GRAPH_TOP=(DeviceInfo.isDevicePhone()?145:186);
public static final int GRAPH\u DISPLAY\u HEIGHT=GRAPH\u TOP-9;//100将以GRAPH\u DISPLAY\u HEIGHT显示
私有Rect mTextBounds;
//正在点击的图形中的句柄小时数
OnHourSelectListener上的公共接口{
已选择小时无效(int SELECTED POSITION);
}
//温度和降水值
私有ArrayList MPRecipationValues=新ArrayList();
private ArrayList mTemperatureValues=new ArrayList();
//天气的当前城市-为此城市每小时检索一次
私人城市McCurrentCityWeather;
//图标
私有HashMap mWeatherConditionIcons=新HashMap();
沉淀法;
//油漆
私家漆;温度漆;
私人涂料;沉淀涂料;
私人涂料;沉淀漆;
私人涂料、纸面涂料;
私人涂料;
私人油漆;
私人涂料;
私家漆mTemperatureTextPaint;
私人涂料;沉淀涂料;
私人涂料-精选涂料;
//选择小时后。重新绘制图表,更新条件
私人int mSelectedPosition;
私有OnHourSelectionListener mOnHourSelectionListener;
private GestureDetector.SimpleOnGestureListener gestureListener=新建gestureListener();
私有最终GestureDetector GestureDetector=新的GestureDetector(getContext(),gestureListener);
//在图形中显示单个小时的宽度(x值)
私有整数mHalfHourlyDisplayWidth;
私有int mHourlyDisplayWidth;
私有整数加总;
私有int IContopadding;
私有int添加;
私有整数预加;
带填充的专用内部高度;
私人国际会议中心;
私有RectF-mIconRectF;
私人整数小时=24;
私人涂料背景涂料;
专用路径tempGeneratePath;
私人MyNews MyNews;
ArrayList路径值;
公共天气小时ForecastGraphView(上下文){
超级(上下文);
}
公共天气小时ForeCastGraphView(上下文上下文,属性集属性){
超级(上下文,attrs);
}
公共WeatherHourlyForecastGraphView(上下文上下文、属性集属性、int defStyleAttr){
super(上下文、attrs、defStyleAttr);
}
@TargetApi(Build.VERSION\u code.LOLLIPOP)
public WeatherHourlyForecastGraphView(上下文上下文、属性集属性、int-defStyleAttr、int-defStyleRes){
super(context、attrs、defStyleAttr、defStyleRes);
}
{
mIconRectF=新的RectF();
backgroundPaint=新油漆();
tempGeneratePath=新路径();
myNews=myNews.getMyNews();
pathValues=新的ArrayList();
初始化paintsinus();
初始化尺寸();
mpReciptionion=BitmapFactory.decodeResource(getResources(),R.drawable.rain\u drop);
mTextBounds=new Rect();//用于在绘图时计算文本边界。在构造函数中初始化以避免在onDraw()中创建对象
//setLayerType(层类型软件,空);
}
private void initializePaintSinsuse()的初始值{
MTTemperaturePaint=新油漆(油漆.防油漆别名标志);
mTemperaturePaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u temperature\u color));
M温度油漆。设定行程宽度(3);
MTTemperaturePaint.setStyle(绘制样式笔划);
MPRecipationPaint=新油漆(油漆.防油漆别名标志);
mpRecipationPaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u precip\u color));
M沉淀漆。设置行程宽度(3);
MPRecipationLinePaint=新油漆(油漆.防油漆别名标志);
mpRecipationLinePaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u precip\u line\u color));
M沉淀在线涂漆。设置行程宽度(3);
MPRecipationLinePaint.setStyle(绘制.样式.笔划);
mGraphPaperLinePaint=新油漆(油漆.防油漆别名标志);
mGraphPaperLinePaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u paper\u line\u color));
调整行程宽度(1);
mGraphPaperLinePaint.setPathEffect(null);
mGraphPaperLinePaint.setStyle(Paint.Style.STROKE);
mWhitePaint=新油漆(油漆。防油漆别名标志);
mWhitePaint.setColor(Color.WHITE);
mWhitePaint.setStrokeWidth(DeviceInfo.getValuesInPixel(1));
mSelectedPaint=新油漆(油漆.防油漆别名标志);
mSelectedPaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u selected\u item\u color));
M选择的油漆。设置行程宽度(2);
mDotPaint=新油漆(油漆.防油漆别名标志);
setColor(ContextCompat.getColor(getContext(),R.color.pale_blue));
mDotPaint.设定行程宽度(2);
mTimeTextPaint=新油漆(油漆.防油漆别名标志);
mTimeTextPaint.setColor(ContextCompat.getColor(getContext(),R.color.hourly\u forecast\u graph\u time\u text\u color));
mTimeTextPaint.setTextSize(getResources().getDimension(R.dimen.hourly\u forecast\u graph\u time\u text\u size));