Related
I have few custom hexagon views in my fragment and upon double tapping on hexagon view,it opens up 2 Activties. I have tried several things to avoid this behaviour and none of it seems to work. Not sure why following things are not working.
I have tried all the things in this post Android Preventing Double Click On A Button and none of them is working in my case. I am not sure what is so weird in this specific case.
First thing i have tried is, by making Activties launch mode set to "singleTop". That didn't work. So i tried others such as singleInstance, singleTask.
Then i tried to disable view on click of hexagon and enable it in onResume.
Even that didnt work. Tried saving a last click time when clicking. Even that didnt work.
Here is my code in fragment.
public class MenuFragment extends BaseFragment implements
OnHexagonClickListener{
....
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_land_menu, container, false);
ScrollView scroll_hexagon = view.findViewById(R.id.scroll_hexagon);
isFirst = true;
loadMenuData();
scroll_hexagon.post(new Runnable() {
#Override
public void run() {
int design_hexagon_width = getResources().getDimensionPixelSize(R.dimen.dimen_190dp);
int design_hexagon_height = getResources().getDimensionPixelSize(R.dimen.dimen_160dp);
int design_padding_horizontal = getResources().getDimensionPixelSize(R.dimen.dimen_5dp);
int design_margin_horizontal = getResources().getDimensionPixelSize(R.dimen.dimen_3dp);
int design_margin_vertical = getResources().getDimensionPixelSize(R.dimen.dimen_5dp);
int design_margin_top = 0; //getResources().getDimensionPixelSize(R.dimen.dimen_5dp);
int scroll_width = scroll_hexagon.getWidth();
int hexagon_width = (int) (((scroll_width - design_padding_horizontal * 2) * 1.105f - design_margin_horizontal) / 2);
int hexagon_margin_horizontal = hexagon_width - (int) ((scroll_width - design_padding_horizontal * 2) * 0.105f) + design_margin_horizontal;
float scale = (float) hexagon_width / (float) design_hexagon_width;
int hexagon_margin_vertical = ((int) (design_hexagon_height * scale) + design_margin_vertical) / 2;
RelativeLayout hexagon_layout = view.findViewById(R.id.lyt_hexagon);
for (int index = 0; index < mHexagonList.size(); index++) {
Hexagon hexagon = mHexagonList.get(index);
HexagonView hexagon_view = new HexagonView(activity, hexagon_width, hexagon_width);
hexagon_view.setId(index);
RelativeLayout.LayoutParams layoutparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutparams.topMargin = design_margin_top + hexagon_margin_vertical * hexagon.getRowIndex();
layoutparams.leftMargin = design_padding_horizontal + hexagon_margin_horizontal * hexagon.getColIndex();
hexagon_view.setLayoutParams(layoutparams);
hexagon_view.setData(hexagon.getType());
hexagon_view.setOnHexagonClickListener(MenuFragment.this);
hexagon_layout.addView(hexagon_view);
}
hexagon_layout.requestLayout();
}
});
return view;
}
#Override
public void onClickHexagon(View view) {
Hexagon hexagon = mHexagonList.get(view.getId());
switch (hexagon.getType()) {
case HexagonType.HEXAGON_PROFILE:
startActivity(new Intent(activity, DashBoard.class));
getActivity().overridePendingTransition(R.anim.anim_left_to_right, R.anim.anim_scale_out);
break;
....
}
Code for my custom view :
public class HexagonView extends View implements Animation.AnimationListener {
private Paint mPaintBrush, mPaintText;
private LassoUtils mLassoUtils = null;
private List<PointF> mPointList = null;
private int mType;
private int mWidth, mHeight;
private int mWidthSpec, mHeightSpec;
private int mBackWidthSpec, mBackHeightSpec;
private int mThumbWidthSpec, mThumbHeightSpec, mThumbMarginSpec;
private int mIconWidthSpec, mIconHeightSpec, mIconMarginSpec;
private int mTextSize, mTextMarginSpec;
private String mText = "";
private Resources mResource;
private Bitmap mBackgroundBitmap = null;
private Bitmap mThumbBitmap = null;
private Bitmap mIconBitmap = null;
private OnHexagonClickListener mListener = null;
private Animation mAnim = null;
private boolean islasso = false;
private boolean isAnimating = false;
public HexagonView(Context context) {
this(context, null);
}
public HexagonView(Context context, AttributeSet attr) {
super(context, attr);
}
public HexagonView(Context context, int width, int height) {
super(context, null);
mType = HexagonType.HEXAGON_NONE;
mResource = context.getResources();
mWidthSpec = width;
mHeightSpec = height;
float scale = (float) width / (float) mResource.getDimensionPixelSize(R.dimen.dimen_190dp);
mBackWidthSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_190dp) * scale);
mBackHeightSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_160dp) * scale);
mThumbWidthSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_185dp) * scale);
mThumbHeightSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_171dp) * scale);
mThumbMarginSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_10dp) * scale);
mThumbMarginSpec = 0;
mTextSize = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_12sp) * scale);
mTextMarginSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_38dp) * scale);
mIconWidthSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_46dp) * scale);
mIconHeightSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_44dp) * scale);
mIconMarginSpec = (int) (mResource.getDimensionPixelSize(R.dimen.dimen_3dp) * scale);
int backColor = mResource.getColor(R.color.transparent);
int textColor = mResource.getColor(R.color.color_text_white);
mBackgroundBitmap = getBitmap(mResource.getDrawable(R.drawable.ic_wk_hexagon_bg), mBackWidthSpec, mBackHeightSpec);
mPaintBrush = new Paint();
mPaintBrush.setAntiAlias(true);
mPaintBrush.setStyle(Paint.Style.FILL);
mPaintBrush.setColor(backColor);
mPaintText = new Paint();
mPaintText.setAntiAlias(true);
mPaintText.setTextSize(mTextSize);
mPaintText.setStyle(Paint.Style.FILL);
mPaintText.setTextAlign(Paint.Align.CENTER);
mPaintText.setColor(textColor);
mAnim = AnimationUtils.loadAnimation(context, R.anim.hexagon_click);
mAnim.setAnimationListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(mWidthSpec), MeasureSpec.getSize(mHeightSpec));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
if (mWidth == 0 || mHeight == 0)
return;
int lenght = mWidth / 2;
double randian30 = 30 * Math.PI / 180;
float a = (float) (lenght * Math.sin(randian30));
float b = (float) (lenght * Math.cos(randian30));
float c = (mHeight - 2 * b) / 2;
Path path = new Path();
path.moveTo(mWidth, mHeight / 2);
path.lineTo(mWidth - a, mHeight - c);
path.lineTo(mWidth - a - lenght, mHeight - c);
path.lineTo(0, mHeight / 2);
path.lineTo(a, c);
path.lineTo(mWidth - a, c);
path.close();
canvas.drawPath(path, mPaintBrush);
if (mBackgroundBitmap != null)
canvas.drawBitmap(mBackgroundBitmap, (float) (mWidth / 2 - mBackWidthSpec / 2), (float) (mHeight / 2 - mBackHeightSpec / 2), null);
if (mThumbBitmap != null)
canvas.drawBitmap(mThumbBitmap, (float) (mWidth / 2 - mThumbWidthSpec / 2), (float) (mHeight / 2 - mThumbHeightSpec / 2 + mThumbMarginSpec), null);
if (mIconBitmap != null)
canvas.drawBitmap(mIconBitmap, (float) (mWidth / 2 - mIconWidthSpec / 2), (float) (mHeight / 2 - mIconHeightSpec + mIconMarginSpec), null);
if (!mText.isEmpty())
canvas.drawText(mText, (float) (mWidth / 2), (float) (mHeight / 2 + mTextSize + mTextMarginSpec), mPaintText);
if (mPointList == null)
mPointList = new ArrayList<>();
else
mPointList.clear();
PointF pf = new PointF();
pf.set(mWidth, mHeight / 2);
mPointList.add(pf);
PointF pf1 = new PointF();
pf1.set(mWidth - a, mHeight - c);
mPointList.add(pf1);
PointF pf2 = new PointF();
pf2.set(mWidth - a - lenght, mHeight - c);
mPointList.add(pf2);
PointF pf3 = new PointF();
pf3.set(0, mHeight / 2);
mPointList.add(pf3);
PointF pf4 = new PointF();
pf4.set(a, c);
mPointList.add(pf4);
PointF pf5 = new PointF();
pf5.set(mWidth - a, c);
mPointList.add(pf5);
if (mLassoUtils == null)
mLassoUtils = new LassoUtils();
mLassoUtils.setLassoList(mPointList);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
islasso = mLassoUtils.contains(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
islasso = mLassoUtils.contains(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
if (isAnimating)
break;
if (mType == HexagonType.HEXAGON_NONE)
break;
this.startAnimation(mAnim);
break;
}
return true;
}
#Override
public void onAnimationStart(Animation animation) {
isAnimating = true;
}
#Override
public void onAnimationEnd(Animation animation) {
if (islasso) {
if (mListener != null)
mListener.onClickHexagon(this);
}
islasso = false;
isAnimating = false;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
public void setData(int type) {
mType = type;
Drawable originThumbDrawable = null;
Drawable originIconDrawable = null;
switch (type) {
case HexagonType.HEXAGON_PROFILE:
originThumbDrawable = mResource.getDrawable(R.mipmap.land_hexagon_profile);
originIconDrawable = mResource.getDrawable(R.mipmap.land_icon_profile);
mText = mResource.getString(R.string.landing_profile);
break;
case HexagonType.HEXAGON_LEARNING:
originThumbDrawable = mResource.getDrawable(R.mipmap.land_hexagon_learning);
originIconDrawable = mResource.getDrawable(R.mipmap.land_icon_learning);
mText = mResource.getString(R.string.landing_learning);
break;
}
if (originThumbDrawable != null)
mThumbBitmap = getBitmap(originThumbDrawable, mThumbWidthSpec, mThumbHeightSpec);
if (originIconDrawable != null)
mIconBitmap = getBitmap(originIconDrawable, mIconWidthSpec, mIconHeightSpec);
}
public void setOnHexagonClickListener(OnHexagonClickListener listener) {
this.mListener = listener;
}
private Bitmap getBitmap(Drawable drawable, int width, int height) {
Canvas canvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return Bitmap.createScaledBitmap(bitmap, width, height, false);
}
}
Heres the best hack, double clicks happen in less than a second and you can disable the click in the span of one second after the first click. Add a global variable on the class
public static boolean clickable = true;
Toggle it during the click and it works great
if (!clickable){
return;
}
clickable = false;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
clickable = true;
}
}, 1000);
//continue with your click code
public boolean clickable = true;
#Override
public void onAnimationEnd(Animation animation) {
if (islasso) {
if (mListener != null && clickable) {
clickable = false;
mListener.onClickHexagon(this);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
clickable = true;
}
}, 1500);
}
}
islasso = false;
isAnimating = false;
}
try this
I want to set border to circular Image view and also want the image view to be inside the circle, so below is my code-
public static Bitmap getRoundedRectBitmap(Bitmap bitmap, int pixels) {
Bitmap result = null;
try {
result = Bitmap.createBitmap(pixels, pixels, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
Rect rect = new Rect(0, 0, pixels*2, pixels*2);
// paint.setAntiAlias(true);
//; canvas.drawARGB(0, 0, 0, 0);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawCircle(pixels/2, pixels/2, pixels/2, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(15);
paint.setColor(Color.parseColor("#17B3F0"));
canvas.drawCircle(pixels/2+0.7f, pixels/2+0.7f, pixels/2+0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
} catch (NullPointerException e) {
} catch (OutOfMemoryError o) {
}
return result;
}
So the problem is with the line
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
If I am commenting this line, it's making all the properties added to paint object visible but making image comes out of the circle.
But I want the Image to be inside the circle as well as a border to be set.
Any help is appreciated.Thanks.
Updated from Here -
I have created a custom class which MyCustomAdapter and Inside getView I am setting the imageView onStateChangeListener as below -
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
View view = inflater.inflate(type == List_Section ? R.layout.section : R.layout.listitem, parent, false);
if (type == List_Section) {
departments = ((TextView) view.findViewById(R.id.tv_li_section));
departments.setText(list_complete_data.get(position).toString());
} else {
//StateListDrawable stateListDrawable = (StateListDrawable) view.getBackground();
Emp_name = (TextView) view.findViewById(R.id.emp_name);
Emp_number = (TextView) view.findViewById(R.id.emp_no);
small_profile =(CircleImageView)view.findViewById(R.id.small_profile);
Log.e(TAG,"small_profile====="+small_profile);
Log.e(TAG,"inside getView=====");
small_profile.setOnStateChangedListener(new OnStateChangeListener() {
#Override
public void onStateChanged(int state) {
Log.e(TAG,"onStateistener and state=="+state+".,,,R.attr.state_on==="+R.attr.state_on+",,,R.attr.state_off=="+R.attr.state_off);
switch (state){
case R.attr.state_on :
small_profile.setBorderColor(Color.parseColor("#ffffff"));
break;
case R.attr.state_off:
small_profile.setBorderColor(Color.parseColor("#000000"));
break;
}
}
});
EDIT : I have made some changes to the code according to your requirements:
add the following interface to your project:
public interface OnStateChangeListener {
void onStateChanged(int state);
}
and then make then I made some changes in the CircleImageView
public class CircleImageView extends ImageView implements View.OnTouchListener {
private static final int[] mStates = { R.attr.state_notset, R.attr.state_on, R.attr.state_off };
private int mStateIndex = 0; // first state is "notset"
private OnStateChangeListener mListener;
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false;
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private final Paint mFillPaint = new Paint();
private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private int mFillColor = DEFAULT_FILL_COLOR;
private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;
private float mDrawableRadius;
private float mBorderRadius;
private ColorFilter mColorFilter;
private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay;
public CircleImageView(Context context) {
super(context);
init();
}
public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
a.recycle();
init();
}
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
#Override
public ScaleType getScaleType() {
return SCALE_TYPE;
}
#Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != SCALE_TYPE) {
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
}
}
#Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
}
#Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) {
return;
}
if (mFillColor != Color.TRANSPARENT) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
}
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(#ColorInt int borderColor) {
if (borderColor == mBorderColor) {
return;
}
mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
}
public void setBorderColorResource(#ColorRes int borderColorRes) {
setBorderColor(getContext().getResources().getColor(borderColorRes));
}
public int getFillColor() {
return mFillColor;
}
public void setFillColor(#ColorInt int fillColor) {
if (fillColor == mFillColor) {
return;
}
mFillColor = fillColor;
mFillPaint.setColor(fillColor);
invalidate();
}
public void setFillColorResource(#ColorRes int fillColorRes) {
setFillColor(getContext().getResources().getColor(fillColorRes));
}
public int getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
}
mBorderWidth = borderWidth;
setup();
}
public boolean isBorderOverlay() {
return mBorderOverlay;
}
public void setBorderOverlay(boolean borderOverlay) {
if (borderOverlay == mBorderOverlay) {
return;
}
mBorderOverlay = borderOverlay;
setup();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
}
#Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
}
#Override
public void setImageResource(#DrawableRes int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
#Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
setup();
}
#Override
public void setColorFilter(ColorFilter cf) {
if (cf == mColorFilter) {
return;
}
mColorFilter = cf;
applyColorFilter();
invalidate();
}
#Override
public ColorFilter getColorFilter() {
return mColorFilter;
}
private void applyColorFilter() {
if (mBitmapPaint != null) {
mBitmapPaint.setColorFilter(mColorFilter);
}
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void setup() {
if (!mReady) {
mSetupPending = true;
return;
}
if (getWidth() == 0 && getHeight() == 0) {
return;
}
if (mBitmap == null) {
invalidate();
return;
}
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mFillPaint.setStyle(Paint.Style.FILL);
mFillPaint.setAntiAlias(true);
mFillPaint.setColor(mFillColor);
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
mBorderRect.set(0, 0, getWidth(), getHeight());
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
applyColorFilter();
updateShaderMatrix();
invalidate();
}
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace+1);
int [] state = { mStates[mStateIndex] };
mergeDrawableStates(drawableState, state);
return drawableState;
}
public void setOnStateChangedListener(OnStateChangeListener l) {
this.mListener = l;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("STATETOUCH", "onTouch: down");
mStateIndex = 1;
if(mListener != null) mListener.onStateChanged(mStates[1]);
return true;
case MotionEvent.ACTION_UP:
Log.e("STATETOUCH", "onTouch: up");
mStateIndex = 2;
if(mListener != null) mListener.onStateChanged(mStates[2]);
return false;
}
return false;
}
}
EDIT in attrs.xml:
and this in attrs.xml
<declare-styleable name="CircleImageView">
<attr name="civ_border_width" format="dimension" />
<attr name="civ_border_color" format="color" />
<attr name="civ_border_overlay" format="boolean" />
<attr name="civ_fill_color" format="color" />
<attr name="state_on" format="boolean" />
<attr name="state_off" format="boolean" />
</declare-styleable>
and use it like this in your code :
<com.yourpackagename.CircleImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:id="#+id/img"
app:civ_border_width="1dp"
app:civ_border_color="#000"
android:src="#drawable/profile_pic"/>
and then in your code do the following:
yourImageView.setOnStateChangedListener(new OnStateChangeListener() {
#Override
public void onStateChanged(int state) {
switch (state){
case R.attr.state_on:
yourImageView.setBorderColor(Color.parseColor("#00ff00"));
break;
case R.attr.state_off:
yourImageView.setBorderColor(Color.parseColor("#ff00ff"));
//do anything you would do after onclick of this imageview here, like open a new activity.
break;
}
Log.e("STATECHANGE", "State changed to: " + context.getResources().getResourceEntryName(state));
}
});
reference : https://stackoverflow.com/a/21969864/2251837
As you can see this code works fine for me. Can you post your code, how you did it, then may be I will be able to help?
EDIT: This is what you need to change in your getView() method :
view.setOnTouchListener(new View.OnTouchListener() { // here view is this view : View view = inflater.inflate(type == List_Section ? R.layout.section : R.layout.listitem, parent, false);
#Override
public boolean onTouch(View v, MotionEvent event) {
small_profile.dispatchTouchEvent(event);
return false;
}
});
You need to draw 2 bitmap here to activate Xfermode:
private Bitmap maskingImage(Bitmap s1, Bitmap s2) {
Bitmap result = Bitmap.createBitmap(s2.getWidth(), s2.getHeight(), Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(s1, 0, 0, null);
mCanvas.drawBitmap(s2, 0, 0, paint);
paint.setXfermode(null);
return result;
}
s2 is ur circle, s1 is ur image. If you want to change to Mode.SRC_IN, just swap s1 and s2 param position
Can someone please explain to me how to implement a progress bar with a divider just like its shown on the image below?
For the progress bar I am using https://github.com/akexorcist/Android-RoundCornerProgressBar but this does not seem to have a divider option.
replace ProgressDrawable from my answer with the modified one:
class ProgressDrawable extends Drawable {
private static final int NUM_SEGMENTS = 4;
private final int mForeground;
private final int mBackground;
private final Paint mPaint = new Paint();
private final RectF mSegment = new RectF();
public ProgressDrawable(int fgColor, int bgColor) {
mForeground = fgColor;
mBackground = bgColor;
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas) {
float level = getLevel() / 10000f;
Rect b = getBounds();
float gapWidth = b.height() / 2f;
float segmentWidth = (b.width() - (NUM_SEGMENTS - 1) * gapWidth) / NUM_SEGMENTS;
mSegment.set(0, 0, segmentWidth, b.height());
mPaint.setColor(mForeground);
for (int i = 0; i < NUM_SEGMENTS; i++) {
float loLevel = i / (float) NUM_SEGMENTS;
float hiLevel = (i + 1) / (float) NUM_SEGMENTS;
if (loLevel <= level && level <= hiLevel) {
float middle = mSegment.left + NUM_SEGMENTS * segmentWidth * (level - loLevel);
canvas.drawRect(mSegment.left, mSegment.top, middle, mSegment.bottom, mPaint);
mPaint.setColor(mBackground);
canvas.drawRect(middle, mSegment.top, mSegment.right, mSegment.bottom, mPaint);
} else {
canvas.drawRect(mSegment, mPaint);
}
mSegment.offset(mSegment.width() + gapWidth, 0);
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
and create it like this:
Drawable d = new ProgressDrawable(0xdd00ff00, 0x4400ff00);
/**
* Created by nagendra on 16/06/15.
*/
public class ProgressBarDrawable extends Drawable {
private int parts = 10;
private Paint paint = null;
private int fillColor = Color.parseColor("#2D6EB9");
private int emptyColor = Color.parseColor("#233952");
private int separatorColor = Color.parseColor("#FFFFFF");
private RectF rectFill = null;
private RectF rectEmpty = null;
private List<RectF> separators = null;
public ProgressBarDrawable(int parts)
{
this.parts = parts;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.separators = new ArrayList<RectF>();
}
#Override
protected boolean onLevelChange(int level)
{
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas)
{
// Calculate values
Rect b = getBounds();
float width = b.width();
float height = b.height();
int spaceFilled = (int)(getLevel() * width / 10000);
this.rectFill = new RectF(0, 0, spaceFilled, height);
this.rectEmpty = new RectF(spaceFilled, 0, width, height);
int spaceBetween = (int)(width / 100);
int widthPart = (int)(width / this.parts - (int)(0.9 * spaceBetween));
int startX = widthPart;
for (int i=0; i<this.parts - 1; i++)
{
this.separators.add( new RectF(startX, 0, startX + spaceBetween, height) );
startX += spaceBetween + widthPart;
}
// Foreground
this.paint.setColor(this.fillColor);
canvas.drawRect(this.rectFill, this.paint);
// Background
this.paint.setColor(this.emptyColor);
canvas.drawRect(this.rectEmpty, this.paint);
// Separator
this.paint.setColor(this.separatorColor);
for (RectF separator : this.separators)
{
canvas.drawRect(separator, this.paint);
}
}
#Override
public void setAlpha(int alpha)
{
}
#Override
public void setColorFilter(ColorFilter cf)
{
}
#Override
public int getOpacity()
{
return PixelFormat.TRANSLUCENT;
}
}
in XM Layout
<ProgressBar
android:id="#+id/progress_bar_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:progress="10"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
ProgressBar progressBar= (ProgressBar)findViewById(R.id.progress_bar_test);
ProgressBarDrawable bgProgress= new ProgressBarDrawable(5);
progressBar.setProgressDrawable(bgProgress);
With the help of this and this answers, I could create my customized version of the segmented horizontal progress bar.
First, Create a class as follows.
public class SegmentedProgressDrawable extends Drawable {
private int parts;
private Paint paint;
private int fillColor;
private int emptyColor;
private int cutOffWidth;
private int separatorColor;
public SegmentedProgressDrawable(int parts, int fillColor, int emptyColor, int separatorColor) {
this.parts = parts;
this.fillColor = fillColor;
this.emptyColor = emptyColor;
this.separatorColor = separatorColor;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(#NonNull Canvas canvas) {
// Calculate values
Rect bounds = getBounds();
float actualWidth = bounds.width();
float actualHeight = bounds.height();
//width with dividers + segment width
int fullBlockWidth = (int) (actualWidth / this.parts);
//ToDo: to change the width of segment change this line
int segmentWidth = (int) (fullBlockWidth * 0.2f);
// int dividerWidth =fullBlockWidth-segmentWidth;
cutOffWidth = (int) (getLevel() * actualWidth / 10000);
//Draw separator as background
RectF fullBox = new RectF(0, 0, actualWidth, actualHeight);
this.paint.setColor(this.separatorColor);
canvas.drawRect(fullBox, this.paint);
//start drawing lines as segmented bars
int startX = 0;
for (int i = 0; i < this.parts; i++) {
int endX = startX + segmentWidth;
//in ideal condition this would be the rectangle
RectF part = new RectF(startX, 0, endX, actualHeight);
//if the segment is below level the paint color should be fill color
if ((startX + segmentWidth) <= cutOffWidth) {
this.paint.setColor(this.fillColor);
canvas.drawRect(part, this.paint);
}
//if the segment is started below the level but ends above the level than we need to create 2 different rectangle
else if (startX < cutOffWidth) {
RectF part1 = new RectF(startX, 0, cutOffWidth, actualHeight);
this.paint.setColor(this.fillColor);
canvas.drawRect(part1, this.paint);
RectF part2 = new RectF(cutOffWidth, 0, startX + segmentWidth, actualHeight);
this.paint.setColor(this.emptyColor);
canvas.drawRect(part2, this.paint);
}
//if the segment is above level the paint color should be empty color
else {
this.paint.setColor(this.emptyColor);
canvas.drawRect(part, this.paint);
}
//update the startX to start the new segment with the gap of divider and segment width
startX += fullBlockWidth;
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
And I, used it as follows:
horizontalProgressBar = findViewById(R.id.horizontal_progress_bar);
int fillColor = ContextCompat.getColor(getActivity(), R.color.primary);
int emptyColor = ContextCompat.getColor(getActivity(), R.color.color_redeem_badge_bg);
int separatorColor = ContextCompat.getColor(getActivity(), R.color.transparent);
SegmentedProgressDrawable progressDrawable = new SegmentedProgressDrawable(20, fillColor, emptyColor, separatorColor);
horizontalProgressBar.setProgressDrawable(progressDrawable);
horizontalProgressBar.setProgress(60);
I want to make my custom view be clicked.And now I do not know which is wrong,when I click it
,it happens nothing.Thanks in advance.This view is that I use canvas to draw a ring,and I want the inside of the ring can be clicked.
public class CircleProgressBar extends View{
OnClickListener progressButton;
private int hour;
private int maxProgress = 24;
private int progress;
int progress1;
private int progressStrokeWidth = 32;
RectF oval;
Paint paint;
Paint paint1;
public CircleProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
oval = new RectF();
paint = new Paint();
paint1 = new Paint();
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Calendar c=Calendar.getInstance();
hour = c.get(Calendar.HOUR_OF_DAY);
progress = hour;
int width = this.getWidth();
int height = this.getHeight();
if(width != height){
int min = Math.min(width, height);
width = min;
height = min;
}
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setColor(Color.LTGRAY);
canvas.drawColor(Color.TRANSPARENT);
paint.setStrokeWidth(15);
paint.setStyle(Style.STROKE);
oval.left = progressStrokeWidth / 2; // 左上角x
oval.top = progressStrokeWidth / 2; // 左上角y
oval.right =height - progressStrokeWidth / 2; // 左下角x
oval.bottom = height - progressStrokeWidth / 2; // 右下角y
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
canvas.drawArc(oval, -90, 360, false, paint);
paint.setColor(Color.rgb(0x57, 0x87, 0xb6));
paint.setStrokeCap(Paint.Cap.ROUND);
if(progress == 9){
canvas.drawArc(oval, -90, 134, false, paint);
}else{
canvas.drawArc(oval, -90, (long)(((float) progress / maxProgress) * 360), false, paint);
}
paint.setColor(Color.CYAN);
paint.setAntiAlias(true);
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
canvas.drawArc(oval, -90, 135+0.5f, false, paint);
paint.setStrokeWidth(15);
String text = (long)(((float) progress / maxProgress) * 100) + "%";
int textHeight = height / 4;
paint.setTextSize(textHeight);
int textWidth = (int) paint.measureText(text, 0, text.length());
paint.setStyle(Style.FILL);
canvas.drawText(text, width / 2 - textWidth / 2, height / 2 +textHeight/2, paint);
}
public int getMaxProgress() {
return maxProgress;
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public void setProgress(int progress) {
this.progress = progress;
this.invalidate();
}
public void setProgressNotInUiThread(int progress) {
this.progress = progress;
this.postInvalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event){
if(event.getAction() == MotionEvent.ACTION_DOWN){
}
return true;
}
This is my main activity.
public class MainActivity extends Activity{
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.circle_fit);
CircleProgressBar progressBar = (CircleProgressBar) findViewById(R.id.circleProgressbar);
progressBar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(MainActivity.this, "hello", Toast.LENGTH_SHORT).show();
}
});
here is my xml
<com.example.circlefit.CircleProgressBar
android:id="#+id/circleProgressbar"
android:layout_width="200dp"
android:layout_height="200dp" />
Remove the touch listener in your custom class. It is ovveriding the click functionality :-
/
/Remove this piece of code from your class and it will work just fine
#Override
public boolean onTouchEvent(MotionEvent event){
if(event.getAction() == MotionEvent.ACTION_DOWN){
}
return true;
}
Add this in your XML:
android:clickable="true"
android:focusable="true"
try like this:
private OnClickListener clickListener;
private boolean isMoved;
#Override
public void setOnClickListener(OnClickListener l) {
this.clickListener = l;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isMoved = false;
if (isValidtouchAndIsInsideCircle(event)) {
return true;
} else {
return false;
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
isMoved = true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
if (clickListener != null && !isMoved) {
clickListener.onClick(this);
}
isMoved = false;
}
return false;
}
private boolean isValidtouchAndIsInsideCircle(MotionEvent event) {
int xPoint = (int) event.getX();
int yPoint = (int) event.getY();
// here validate the x and y points with your circle coordinates if it
// is in circle return true or else return false
return false;
}
I'm creating custom ListView. Adapter:
public class CustomListEventsAdapter extends BaseAdapter {
private Context context;
private List<Map<String, String>> values;
public CustomListEventsAdapter(Context context, List<Map<String, String>> values) {
this.context = context;
this.values = values;
}
#Override
public int getCount() {
return values.size();
}
#Override
public Object getItem(int position) {
return values.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final CustomItemForEventsList view = new CustomItemForEventsList(context, values.get(position), (long) position);
int itemHeight;
if (parent.getWidth() > parent.getHeight()) {
itemHeight = parent.getHeight();
} else {
itemHeight = parent.getHeight() / 2;
}
view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, itemHeight));
return view;
}}
Custom View for ListView's item:
public class CustomItemForEventsList extends View {
private MainActivity context;
private Map<String,String> values;
private long id;
private Paint pMainText;
private int viewWidth;
private int viewHeight;
private boolean isLandscape;
private Bitmap backgroundBitmap;
private RectF bitmapSizes;
private RectF viewSizes;
private Matrix matrix;
public CustomItemForEventsList(final Context context, Map<String,String> values, long id) {
super(context);
this.context = (MainActivity) context;
this.values = values;
this.id = id;
pMainText = new Paint(Paint.ANTI_ALIAS_FLAG);
pMainText.setColor(Color.WHITE);
pMainText.setTextAlign(Paint.Align.LEFT);
bitmapSizes = new RectF();
viewSizes = new RectF();
matrix = new Matrix();
loadBackgroundBitmap(values.get("url"));
}
private void loadBackgroundBitmap(final String url) {
context.imageLoader.loadImage(url, new ImageSize(viewWidth, viewHeight), context.displayImageOptions, new ImageLoadingListener() {
#Override
public void onLoadingStarted(String s, View view) {
}
#Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
// Log.d(Statics.LOG, "onLoadingFailed");
}
#Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
backgroundBitmap = bitmap;
invalidate();
}
#Override
public void onLoadingCancelled(String s, View view) {
// Log.d(Statics.LOG, "onLoadingCanceled");
loadBackgroundBitmap(url);
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
isLandscape = viewWidth > viewHeight;
viewSizes.set(0, 0, viewWidth, viewHeight);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawARGB(255, 0, 0, 0);
// Drawing view's background
if (backgroundBitmap != null) {
if (!backgroundBitmap.isRecycled()) {
bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
matrix.setRectToRect(viewSizes, bitmapSizes, Matrix.ScaleToFit.FILL);
matrix.invert(matrix);
canvas.drawBitmap(backgroundBitmap, matrix, null);
}
}
}}
Everything works perfect on new Android's versions (tested on 4.0, 4.2, 4.4), but in 2.3.3 appears troubles with positioning and resizing of ListView items: bitmaps don't resize to view's sizes in onDraw, when we click on ListView or scroll it and release.
Screens from 2.3.3. That's what happening when we touch down ListView, then scroll, then wait for a while and release (that behavior I want to see in all cases):
http://s16.postimg.org/qvf26fb45/image.png
And here is what usually happens, when we click on ListView or scroll and release:
http://s22.postimg.org/d7fd8quc1/image.png
Why does it happen and how can we fix that?
Ok, I found out how to solve that problem in Android 2.3.3. Of course we can check Android's version in code and make two branches: for old and new versions. For new versions use Matrix:
if (!backgroundBitmap.isRecycled()) {
bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
matrix.setRectToRect(viewSizes, bitmapSizes, Matrix.ScaleToFit.CENTER);
matrix.invert(matrix);
canvas.drawBitmap(backgroundBitmap, matrix, null);
}
And for old versions use method, proposed by #pskink in comments. But I don't want to see any differences in different Android versions (fit in old, crop in new), so I offer another way.
Our target - is to avoid operations with Rects and Matrix, which looks like are very expensive and give us some bugs with drawing custom ListView items in Android 2.3.3. So we have to do all upscale operations manually:
public class CustomItemForEventsList extends View {
private MainActivity context;
private int viewWidth;
private int viewHeight;
private Bitmap backgroundBitmap;
private Rect bitmapSizes;
private Rect preferedBitmapSizes;
public CustomItemForEventsList(final Context context, Map<String,String> values, long id) {
super(context);
this.context = (MainActivity) context;
bitmapSizes = new Rect();
preferedBitmapSizes = new Rect();
loadBackgroundBitmap(values.get("url"));
}
private void loadBackgroundBitmap(final String url) {
context.imageLoader.loadImage(url, new ImageSize(viewWidth, viewHeight), context.displayImageOptions, new ImageLoadingListener() {
#Override
public void onLoadingStarted(String s, View view) {
}
#Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
// Log.d(Statics.LOG, "onLoadingFailed");
}
#Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
backgroundBitmap = bitmap;
invalidate();
}
#Override
public void onLoadingCancelled(String s, View view) {
// Log.d(Statics.LOG, "onLoadingCanceled");
loadBackgroundBitmap(url);
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawARGB(255, 0, 0, 0);
// Drawing view's background
if (backgroundBitmap != null) {
if (!backgroundBitmap.isRecycled()) {
bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
setPreferedBitmapSizes();
canvas.drawBitmap(backgroundBitmap, bitmapSizes, preferedBitmapSizes, null);
}
}
}
private void setPreferedBitmapSizes() {
int instBmpWidth = backgroundBitmap.getWidth();
int instBmpHeight = backgroundBitmap.getHeight();
int newBmpWidth, newBmpHeight;
newBmpWidth = viewWidth; newBmpHeight = viewWidth * instBmpHeight / instBmpWidth;
if (viewHeight > newBmpHeight) {
newBmpWidth = instBmpWidth * viewHeight / instBmpHeight; newBmpHeight = viewHeight;
if (newBmpWidth == viewWidth) {
preferedBitmapSizes.set(0, 0, newBmpWidth, newBmpHeight);
} else {
preferedBitmapSizes.set(-((newBmpWidth-viewWidth)/2), 0, viewWidth + ((newBmpWidth-viewWidth)/2), newBmpHeight);
}
} else {
if (newBmpHeight == viewHeight) {
preferedBitmapSizes.set(0, 0, newBmpWidth, newBmpHeight);
} else {
preferedBitmapSizes.set(0, -((newBmpHeight-viewHeight)/2), newBmpWidth, viewHeight + ((newBmpHeight-viewHeight)/2));
}
}
}}
Now the problem is solved:
http://s29.postimg.org/hbv9d0z3b/image.png
UPDATE: We can use much more simpler code (thanks to pskink, it is his solution):
m.reset();
float scaleX = getWidth() / (float) b.getWidth();
float scaleY = getHeight() / (float) b.getHeight();
float scale = Math.max(scaleX, scaleY);
m.postScale(scale, scale);
float dx = (getWidth() - b.getWidth() * scale) / 2;
float dy = (getHeight() - b.getHeight() * scale) / 2;
m.postTranslate(dx, dy);
canvas.drawBitmap(b, m, null);
Where m - Matrix, b - our Bitmap.