Skip to content

Instantly share code, notes, and snippets.

@robbiemu
Last active April 27, 2016 14:29
Show Gist options
  • Save robbiemu/0ad1ae50e0146325c02d to your computer and use it in GitHub Desktop.
Save robbiemu/0ad1ae50e0146325c02d to your computer and use it in GitHub Desktop.
a custom view that indicates wind speed and direction
...
public class DetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private WindView mWindGraphic;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mWindGraphic = (WindView) rootView.findViewById(R.id.detail_wind_graphic);
...
}
...
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
...
// Read wind speed and direction from cursor and update view
float windSpeedStr = data.getFloat(COL_WEATHER_WIND_SPEED);
float windDirStr = data.getFloat(COL_WEATHER_DEGREES);
mWindView.setText(Utility.getFormattedWind(getActivity(), windSpeedStr, windDirStr));
mWindGraphic.setWindSpeed((int) windSpeedStr);
mWindGraphic.setWindOriginDegrees(windDirStr);
mWindGraphic.setForegroundColor(getResources().getColor(R.color.sunshine_light_blue));
mWindGraphic.setBackgroundColor(getResources().getColor(R.color.sunshine_dark_blue));
...
}
}
...
<com.example.android.sunshine.app.WindView
android:id="@+id/detail_wind_graphic"
android:layout_marginTop="4dp"
android:layout_width="120dp"
android:layout_height="120dp" />
...
<vector android:height="24dp" android:viewportHeight="512.0"
android:viewportWidth="512.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M256,17.1c-75.7,0 -137.1,61.4 -137.1,137.1 0.1,23.3 6,46.1 11.6,56.3L256,494.9l120,-274.2h-0.1c11.3,-20.3 17.2,-43.2 17.2,-66.4C393.1,78.5 331.7,17.1 256,17.1zM256,85.7a68.6,68.6 0,0 1,68.6 68.6A68.6,68.6 0,0 1,256 222.8a68.6,68.6 0,0 1,-68.6 -68.6A68.6,68.6 0,0 1,256 85.7z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="172dp"
android:height="172dp"
android:viewportWidth="172.0"
android:viewportHeight="172.0">
<path
android:pathData="M74.29,11.9c-31.98,5 -57.24,30.26 -62.24,62.24l9.09,-2.27c2.64,-12.39 8.78,-23.77 17.95,-32.93c9.17,-9.17 20.55,-15.31 32.93,-17.95L74.29,11.9z"
android:fillColor="#231F20"/>
<path
android:pathData="M97.6,11.9l2.27,9.09c12.39,2.64 23.77,8.78 32.93,17.95c9.16,9.17 15.31,20.55 17.95,32.93l9.09,2.27C154.83,42.17 129.57,16.91 97.6,11.9z"
android:fillColor="#231F20"/>
<path
android:pathData="M72.02,150.6c-12.39,-2.63 -23.77,-8.78 -32.93,-17.95c-9.16,-9.17 -15.31,-20.55 -17.95,-32.93l-9.09,-2.27c5,31.98 30.26,57.24 62.24,62.24L72.02,150.6z"
android:fillColor="#231F20"/>
<path
android:pathData="M150.75,99.72c-2.63,12.39 -8.78,23.77 -17.95,32.93c-9.17,9.17 -20.55,15.31 -32.93,17.95l-2.27,9.09c31.98,-5 57.24,-30.26 62.24,-62.24L150.75,99.72z"
android:fillColor="#231F20"/>
<path
android:pathData="M171.26,85.8L109.62,70.39l16.54,-24.81L101.35,62.12L85.94,0.47L70.53,62.12L45.72,45.58l16.54,24.81L0.62,85.8l61.64,15.41L45.72,126.02l24.81,-16.54l15.41,61.64l15.41,-61.64l24.81,16.54L109.62,101.21L171.26,85.8zM85.94,23.93l10.88,43.52c-3.19,-1.89 -6.91,-2.98 -10.88,-2.98L85.94,23.93zM64.61,85.8L64.61,85.8L24.07,85.8l43.52,-10.88C65.7,78.1 64.61,81.82 64.61,85.8zM85.94,147.66l-10.88,-43.52c3.19,1.89 6.91,2.98 10.88,2.98l0,0L85.94,147.66zM85.94,102.86c-9.41,0 -17.06,-7.66 -17.06,-17.06S76.53,68.73 85.94,68.73c9.41,0 17.07,7.65 17.07,17.06S95.35,102.86 85.94,102.86zM107.27,85.8h40.54l-43.52,10.88C106.18,93.49 107.27,89.77 107.27,85.8z"
android:fillColor="#231F20"/>
<path
android:pathData="M85.94,94.33c-4.7,0 -8.53,-3.83 -8.53,-8.53c0,-4.7 3.83,-8.53 8.53,-8.53s8.53,3.83 8.53,8.53C94.48,90.5 90.65,94.33 85.94,94.33z"
android:fillColor="#231F20"/>
</vector>
package com.example.android.sunshine.app;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* Created by RobertoTomás on 0023, 23/3/2016.
*/
public class WindView extends View {
private static final float GESTURE_THRESHOLD_DIP = 16.0f;
private Bitmap pDirectionArrow;
private Bitmap pCompassRing;
private Drawable pDirectionArrowVector;
private Drawable pCompassRingVector;
private final Paint pSpeedText = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Matrix pMatrix = new Matrix();
private Float pDensity;
private Rect pVectorBoundingRectangle;
private int mForegroundColor;
private int mBackgroundColor;
private int mWindSpeed;
private float mWindOriginDegrees;
public float getWindOriginDegrees() {
return mWindOriginDegrees;
}
public void setWindOriginDegrees(float mWindOriginDegrees) {
this.mWindOriginDegrees = mWindOriginDegrees;
_rotateArrow();
invalidate();
}
public int getForegroundColor() {
return mForegroundColor;
}
/**
* The color expected is the resolved color: `getResources().getcolor(R.color...)`
* @param foregroundColor
*/
public void setForegroundColor(int foregroundColor) {
this.mForegroundColor = foregroundColor;
_changeForegroundColor();
invalidate();
}
private void _changeForegroundColor(){
this.pDirectionArrow = _getBitmap(pDirectionArrowVector, mForegroundColor);
}
public int getBackgroundColor() {
return mBackgroundColor;
}
/**
* The color expected is the resolved color: `getResources().getcolor(R.color...)`
* @param backgroundColor
*/
public void setBackgroundColor(int backgroundColor) {
this.mBackgroundColor = backgroundColor;
_changeBackgroundColor();
invalidate();
}
private void _changeBackgroundColor(){
this.pCompassRing = _getBitmap(pCompassRingVector, mBackgroundColor);
}
public int getWindSpeed() {
return mWindSpeed;
}
public void setWindSpeed(int wind_speed) {
this.mWindSpeed = wind_speed;
invalidate();
}
public String getWindSpeedText (){
return Integer.toString(mWindSpeed) + " km/h";
}
/** Constructors **/
public WindView(Context c){
super(c);
objectHandler(c);
}
public WindView(Context c, AttributeSet attrs) {
super(c, attrs);
objectHandler(c);
}
public WindView(Context c, AttributeSet attrs, int DefaultStyle) {
super(c, attrs, DefaultStyle);
objectHandler(c);
}
public void objectHandler(Context c){
this.mForegroundColor = getResources().getColor(R.color.primary_dark_material_light);
this.mBackgroundColor = getResources().getColor(R.color.primary_material_dark);
this.mWindSpeed = 0;
this.mWindOriginDegrees = 0;
this.pVectorBoundingRectangle = new Rect ();
this.pDensity = getContext().getResources().getDisplayMetrics().density;
this.pCompassRingVector = _getVector(R.drawable.svg_wind_compass_ring);
this.pDirectionArrowVector = _getVector(R.drawable.svg_wind_arrow);
this.pCompassRing = _getBitmap(pCompassRingVector, mBackgroundColor);
this.pDirectionArrow = _getBitmap(pDirectionArrowVector, mForegroundColor);
pSpeedText.setStyle(Paint.Style.FILL);
pSpeedText.setColor(mForegroundColor);
pSpeedText.setAntiAlias(true);
pSpeedText.setTextAlign(Paint.Align.LEFT);
Typeface typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC);
pSpeedText.setTypeface(typeface);
}
private Drawable _getVector(int drawableId) {
Drawable vectorDrawable;
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.LOLLIPOP){
vectorDrawable = getResources().getDrawable(drawableId,null);
}else {
vectorDrawable = getResources().getDrawable(drawableId);
}
return vectorDrawable;
}
private Bitmap _getBitmap(Drawable vectorDrawable, int color) {
int h = vectorDrawable.getIntrinsicHeight();
int w = vectorDrawable.getIntrinsicWidth();
if(pVectorBoundingRectangle.isEmpty()){
pVectorBoundingRectangle.set(0,0, w, h);
} else {
h = pVectorBoundingRectangle.height();
w = pVectorBoundingRectangle.width();
//Setting a pixel default if intrinsic height or width is not found , eg a shape drawable
h=h>0?h:96;
w=w>0?w:96;
if ((h != pVectorBoundingRectangle.height()) || (w != pVectorBoundingRectangle.width())) {
// the rectangle was not empty, but still had a 0 width or height value
pVectorBoundingRectangle.set(0,0, w, h);
}
}
vectorDrawable.setBounds(pVectorBoundingRectangle);
// Wrap the drawable so that future tinting calls work
// on pre-v21 devices. Always use the returned drawable.
Drawable wrapDrawable = DrawableCompat.wrap(vectorDrawable);
DrawableCompat.setTint(wrapDrawable.mutate(), color);
wrapDrawable.setBounds(pVectorBoundingRectangle);
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
wrapDrawable.draw(canvas);
return bm;
}
private Matrix _rotateArrow() {
// normally we'd want (180+mWindOriginDegrees)%360 for the direction since the wind comes _from_
// the orientation given ina weather report. However our graphic image here has the arrow already
// rotated 180 from normal
pMatrix.setRotate(mWindOriginDegrees, pDirectionArrow.getWidth() / 2, pDirectionArrow.getHeight() / 2);
return pMatrix;
}
private int _measureWidth(int measureSpec) {
int preferred = pCompassRing.getWidth() * 2;
return _getMeasurement(measureSpec, preferred);
}
private int _measureHeight(int measureSpec) {
int preferred = pCompassRing.getHeight() * 2;
return _getMeasurement(measureSpec, preferred);
}
private int _getMeasurement(int measureSpec, int preferred) {
int specSize = MeasureSpec.getSize(measureSpec);
int measurement = 0;
switch(MeasureSpec.getMode(measureSpec)) {
case MeasureSpec.EXACTLY:
// This means the width of this view has been given.
measurement = specSize;
break;
case MeasureSpec.AT_MOST:
// Take the minimum of the preferred size and what
// we were told to be.
measurement = Math.min(preferred, specSize);
break;
default:
measurement = preferred;
break;
}
return measurement;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int renderWidth = _measureWidth(widthMeasureSpec);
int renderHeight = _measureHeight(heightMeasureSpec);
pVectorBoundingRectangle.set(0, 0, (int) Math.round(renderHeight * 0.75), (int) Math.round(renderWidth*0.75));
int textSize = (int) (GESTURE_THRESHOLD_DIP * pDensity + 0.5f);
textSize *= pDensity;
textSize /= 4;
pSpeedText.setTextSize(textSize);
setMeasuredDimension(renderWidth, renderHeight);
// Log.d("WindView", "Set a bounding rectangle h:" + renderHeight + " w:" +renderWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(18 * pDensity, 18 * pDensity);
canvas.drawBitmap(pCompassRing, 0, 0, null);
canvas.drawBitmap(pDirectionArrow, _rotateArrow(), null);
String txt = getWindSpeedText();
canvas.drawText(
txt, // Text to draw
0,//x
0,//y
pSpeedText // Paint
);
}
}
@robbiemu
Copy link
Author

this is a custom view that indicates wind direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment