下面是我们用来绘制天气图的代码,该天气图在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;
}
}
}