Android custom view Bitmap memory leak - android

I've got a custom view in which I need to draw two bitmaps, one is a background, representing the image of a map and one is a pin which will be drawn on top/left position in canvas.
The both images are drawn onDraw and remain the same during the live of the activity that contains the view. After a while I get a
OutOfMemoryError: bitmap size exceeds
VM budget
This means that I have a leak and the bitmaps don't get garbage collected. I asked this question before, but now the situation changed a little. I made a init method where I set the bitmaps I want to use but this still isn't a good approach, the error appears later, but still there.
Here is the code
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private int space = 0;
private Bitmap resizedBitmap;
private Bitmap position;
private Bitmap mapBitmap;
public void setMapBitmap(Bitmap value) {
this.mapBitmap = value;
}
public MyMapView(Context context) {
super(context);
}
public MyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(final Context context) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
resizedBitmap = Bitmap.createScaledBitmap(mapBitmap, height, height, true);
position = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.position);
space = (width - resizedBitmap.getWidth()) / 2;
}
public void destroy()
{
resizedBitmap.recycle();
resizedBitmap=null;
position.recycle();
position=null;
mapBitmap.recycle();
mapBitmap=null;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (mapBitmap != null) {
canvas.drawBitmap(resizedBitmap, space, 0, null);
}
if (xPos != 0 && yPos != 0) {
canvas.translate(xPos + space - position.getWidth() / 2, yPos - position.getHeight() / 2);
canvas.drawBitmap(position, new Matrix(), new Paint());
}
}
public void updatePosition(int xpos, int ypos)
{
xPos = xpos;
yPos = ypos;
invalidate();
}
}
I call the setMapBitmap() and init() on onCreate of the activity that contains the view. In there I know what bitmap will be used as map. onPause of the activity I call the destroy() of the view. I still get errors.
I've read this this but I don't know how to adapt it on my case
I AM OPEN TO ANY OTHER SOLUTION. Please note that I need to resize the map bitmap (based on the height of the layout that contains it in mai activity) and still be able to draw the position on correct place.

You obviously have a memory leak. Read how to avoid memory leaks and how to find memory leaks.
Here is a your code refactored to use WeakReference:
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private int space = 0;
private WeakReference<Bitmap> resizedBitmap;
private WeakReference<Bitmap> position;
private WeakReference<Bitmap> mapBitmap;
public void setMapBitmap(Bitmap value) {
this.mapBitmap = new WeakReference<Bitmap>(value);
}
public MyMapView(Context context) {
super(context);
}
public MyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(Bitmap mapBitmap) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
resizedBitmap = new WeakReference<Bitmap>(Bitmap.createScaledBitmap(
mapBitmap, width, height, true));
position = new WeakReference(BitmapFactory.decodeResource(
getContext().getResources(), R.drawable.position));
space = (width - resizedBitmap.get().getWidth()) / 2;
}
// public void destroy() {
// resizedBitmap.recycle();
// resizedBitmap = null;
// position.recycle();
// position = null;
// mapBitmap.recycle();
// mapBitmap = null;
// }
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (mapBitmap != null) {
canvas.drawBitmap(resizedBitmap.get(), space, 0, null);
}
if (xPos != 0 && yPos != 0) {
canvas.translate(xPos + space - position.get().getWidth() / 2,
yPos - position.get().getHeight() / 2);
canvas.drawBitmap(position.get(), new Matrix(), null);
}
}
public void updatePosition(int xpos, int ypos) {
xPos = xpos;
yPos = ypos;
invalidate();
}
}

Something that marked me in your code is that you don't recycle all your bitmaps apparently. Here are a couple of code snippets that could help:
bmp = getBitmapFromRessourceID(R.drawable.menu);
ratio = (float)this.height/(float)bmp.getScaledHeight(canvas);
temp = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth()*ratio), (int) (bmp.getHeight()*ratio-10), false);
bmp.recycle();
bmp = temp;
and:
Bitmap temp = BitmapFactory.decodeResource(context.getResources(), resource, opts);
Bitmap bmp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), flip, true);
temp.recycle();

Related

How to use a custom view in main activity

I'm developing a game which shows shapes randomly, I got the class but I got no idea how to call the custom view from my GameFragment. I was wondering if anyone can help me to achieve it.
CustomView.java
public class CustomViews extends View {
private final int SQUARE_SIZE = 500;
private RectF rectF;
private Paint paint;
private boolean CIRCLE = false;
private boolean RECTANGLE = false;
public CustomViews(Context context) {
super(context);
init(null);
}
public CustomViews(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomViews(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public CustomViews(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(#Nullable AttributeSet set) {
rectF = new RectF();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
randomColorGenerator();
}
public void rect(Canvas canvas) {
int height = canvas.getHeight() / 2;
int width = canvas.getWidth() / 2;
rectF.set(width - 200, height - 200, width + 200, height + 200);
}
private void circle(Canvas canvas) {
float cx;
float cy;
float radius = 200;
cx = canvas.getWidth() / 2;
cy = canvas.getWidth() / 2;
canvas.drawCircle(cx, cy, radius, paint);
}
private void randomColorGenerator() {
Random random = new Random();
int choices = 3;
switch (random.nextInt(choices)) {
case 0:
paint.setColor(Color.GREEN);
break;
case 1:
paint.setColor(Color.RED);
break;
case 2:
paint.setColor(Color.BLACK);
break;
}
}
public void randomShapeGenerator(Canvas canvas) {
Random random = new Random();
int numberOfMethods = 2;
switch (random.nextInt(numberOfMethods)) {
case 0:
rect(canvas);
rectSelected();
break;
case 1:
circle(canvas);
circleSelected();
break;
}
}
public boolean circleSelected(){
RECTANGLE = false;
return CIRCLE = true;
}
public boolean rectSelected( ) {
CIRCLE = false;
return RECTANGLE = true;
}
#Override
public void onDraw(Canvas canvas) {
randomShapeGenerator(canvas);
canvas.drawRect(rectF, paint);
}
}
Now I need to call it from my GameFragment where there are some buttons and whenever you click on them a new random shape must be generate. Is anyone got any idea?
Assuming your fragment has a LinearLayout with id main_content then this might help you to add your custom view programmatically:
Button btn = findViewById(R.id.yourbutton);
btn.setOnClickListener(v -> {
LinearLayout main_layout = findViewById(R.id.main_content);
CustomViews customView = new CustomViews(this);
customView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
main_layout.addView(customView);
});
layoutParams might be also tuned according your needs.
You have to take into account where you want to display the customViews and also their sizes. For instance, if you have a linear layout with vertical orientation, you can add views in the following way:
main_layout.addView(customView, 400, 400);
This will display multiple customViews one below the other (until the screen's height is reached).
Indeed, CustomViews has to be modified a bit (here are two methods to be changed):
public void rect(Canvas canvas) {
int height = canvas.getHeight() / 2;
int width = canvas.getWidth() / 2;
rectF.set(width - 200, height - 200, width + 200, height + 200);
canvas.drawRect(rectF, paint);
}
#Override
public void onDraw(Canvas canvas) {
randomShapeGenerator(canvas);
}

How to change the color of the active tab indicator? (Android Studio)

i want to know if i'm able to change the color of the active tab indicator (that light-blue line) to black(#4A4A4A)
Here is the photo
public class PageIndicator extends View {
int totalNoOfDots;
int activeDot;
int dotSpacing;
int horizontalSpace = 5;
Bitmap activeDotBitmap;
Bitmap normalDotBitmap;
int x = 0;
private Paint paint;
public PageIndicator(Context context) {
super(context);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
activeDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_active);
normalDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_normal);
}
public PageIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
activeDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_active);
normalDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_normal);
}
public PageIndicator(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
activeDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_active);
normalDotBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot_normal);
}
#Override
protected void onDraw(Canvas canvas) {
drawDot(canvas);
super.onDraw(canvas);
}
private void drawDot(Canvas canvas) {
for (int i = 0; i < totalNoOfDots; i++) {
if (i == activeDot) {
canvas.drawBitmap(activeDotBitmap, x, 0, paint);
} else {
canvas.drawBitmap(normalDotBitmap, x, 0, paint);
}
x = x + activeDotBitmap.getWidth() + horizontalSpace + dotSpacing;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = totalNoOfDots * (activeDotBitmap.getWidth() + horizontalSpace + getDotSpacing());
width = resolveSize(width, widthMeasureSpec);
int height = activeDotBitmap.getHeight();
height = resolveSize(height, heightMeasureSpec);
setMeasuredDimension(width, height);
}
public void refersh() {
x = 0;
invalidate();
}
public int getTotalNoOfDots() {
return totalNoOfDots;
}
public void setTotalNoOfDots(int totalNoOfDots) {
this.totalNoOfDots = totalNoOfDots;
x = 0;
invalidate();
}
public int getActiveDot() {
return activeDot;
}
public void setActiveDot(int activeDot) {
this.activeDot = activeDot;
x = 0;
invalidate();
}
public int getDotSpacing() {
return dotSpacing;
}
public void setDotSpacing(int dotSpacing) {
this.dotSpacing = dotSpacing;
x = 0;
invalidate();
}
}
if you are using support library.
add this to your tabLayout
app:tabIndicatorColor="#4A4A4A"

Android: ScrollView's child custom view not getting proper height

I'm writing a custom view, which is having a ScrollView as a parent, because, the custom view height exceeds the screen's height. The problem that I'm facing is that I cannot scroll the layout.
The fragment which is added in the activity's UI:
public class FragmentHour extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
HourView view = new HourView(getActivity());
ScrollView sv = new ScrollView(getActivity());
sv.setFillViewport(true);
sv.addView(view);
return sv;
}
}
The custom view:
public class HourView extends View {
// Default rectangle for one row, used to know the sizes
private Rect mDefRect;
// Paint to draw the lines
private Paint mPaint;
public HourView(Context context) {
super(context);
init();
}
public HourView(Context context, AttributeSet set) {
super(context, set);
init();
}
public HourView(Context context, AttributeSet set, int defStyle) {
super(context, set, defStyle);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(UIUtils.dpToPx(getContext(), 0.5f));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Create a default rectangle, for one row
// We need to recreate the rectangle multiple times, because getWidth() == 0
int height = UIUitls.dpToPx(getContext(), 50); // dpi to px
mDefRect = new Rect(0, 0, getWidth(), height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
int verticalOffset = 0;
for (int i = 0; i < 24; i++) {
int cellHeight = mDefRect.height();
verticalOffset += cellHeight;
canvas.drawLine(0, verticalOffset, getWidth(), verticalOffset, mPaint);
}
}
}
The code above will result:
As I said, the problem is that I cannot scroll the view, probably because the child view's height, it's the size of it's parent.
I have tried to override the onMeasure method:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDefRect != null) {
int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
int newHeight = mDefRect.height() * 24;
setMeasuredDimension(measuredWidth, newHeight);
// super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
I have tried to register for layout-change, then change the Layout parameters:
private void init() {
// ...
post(new Runnable() {
#Override
public void run() {
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
getLayoutParams().height = mDefRect.height() * 24;
// postInvalidate();
}
});
}
});
}

Force redraw of custom view (android)

I just have read similar questions but all answer don't work for me.
I have this custom view:
public class CompassTargetView extends View {
Context mContext;
Bitmap mBmp;
Matrix matrix;
int w, h, bw, bh;
int px = -1, py = -1;
public CompassTargetView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mContext = context;
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.ttriangle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(px == -1 && py == -1){
px=w/2-bw/2;
py=h/2-bh/2;
}
if (matrix != null) {
canvas.drawBitmap(mBmp, matrix, null);
matrix = null;
} else {
canvas.drawBitmap(mBmp, px, py, null);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//registriamo le dimensioni della view
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
public void setTrinagleIcon(){
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.ttriangle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
invalidate();
}
public void setCircleIcon(){
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.tcircle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
invalidate();
}
public void setXY(Integer nx, Integer ny){
px = nx - bw/2;
py = ny - bh/2;
invalidate();
}
public void translateIcon(Integer nx, Integer ny){
matrix = new Matrix();
matrix.reset();
matrix.postTranslate(nx, ny);
invalidate();
}
public void rotateIcon(Integer nx, Integer ny, Float ang){
matrix = new Matrix();
matrix.reset();
matrix.postRotate(ang, nx, ny);
invalidate();
}
}
it show a small icon then I want use setXY, translateIcon and rotateIcon to "animate" the icon. However the onDraw method is call only on view create after this if I call setXY, translateIcon and rotateIcon onDraw is not call.
I just use invalidate(); and I have try setWillNotDraw(false); but this don't work.
Any idea?
Displaying a Toast in the onDraw is a very bad idea since it may cause a call to onDraw which will display the Toast and it will call onDraw.... infinite loop.
So replace the Toast by log (and keep the log only during debugging since onDraw is call very very often) and see what append.
The problem is that you are not updating the member matrix. You are creating a new one !
public void translateIcon(Integer nx, Integer ny){
Matrix matrix = new Matrix(); // <-- WRONG
matrix = new Matrix(); // <-- CORRECT
...
invalidate();
}

Make Coverflow appear like a Horizontal Gallery Android

I am trying to make the CoverFlow below appear like a Horizontal Gallery. The original file looks like this
There are three classes
the CoverFlow.class which extends a gallery
public class CoverFlow extends Gallery {
private Camera mCamera = new Camera();
private int mMaxRotationAngle = 50;
private int mMaxZoom = -380;
private int mCoveflowCenter;
private boolean mAlphaMode = true;
private boolean mCircleMode = false;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
public boolean getCircleMode() {
return mCircleMode;
}
public void setCircleMode(boolean isCircle) {
mCircleMode = isCircle;
}
public boolean getAlphaMode() {
return mAlphaMode;
}
public void setAlphaMode(boolean isAlpha) {
mAlphaMode = isAlpha;
}
public int getMaxZoom() {
return mMaxZoom;
}
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ getPaddingLeft();
}
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth();
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle
: mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle);
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* #param w
* Current width of this view.
* #param h
* Current height of this view.
* #param oldw
* Old width of this view.
* #param oldh
* Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
*
* #param imageView
* ImageView the ImageView whose bitmap we want to rotate
* #param t
* transformation
* #param rotationAngle
* the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t,
int rotationAngle) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();
final int imageHeight = child.getLayoutParams().height;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
// As the angle of the view gets less, zoom in
if (rotation <= mMaxRotationAngle) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
if (mCircleMode) {
if (rotation < 40)
mCamera.translate(0.0f, 155, 0.0f);
else
mCamera.translate(0.0f, (255 - rotation * 2.5f), 0.0f);
}
if (mAlphaMode) {
((ImageView) (child)).setAlpha((int) (255 - rotation * 2.5));
}
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth ), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth ), (imageHeight / 2));
mCamera.restore();
}
}
The second class is my coverFlowActivity which is my class class
public class CoverFlowActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
CoverFlow cf = new CoverFlow(this);
ImageAdapter ia = new ImageAdapter(this);
cf.setAdapter(ia);
cf.setAnimationDuration(1000);
setContentView(cf);
}
public class ImageAdapter extends BaseAdapter {
private int[] mImgs = {
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img5,
R.drawable.img6,
R.drawable.img7,
R.drawable.img8
};
Context mContext;
public ImageAdapter(Context context) {
this.mContext = context;
}
#Override
public int getCount() {
return mImgs.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return mImgs[position];
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ReflectionImage i = new ReflectionImage(mContext);
i.setImageResource(mImgs[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(100, 100));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
}
public float getScale(boolean focused, int offset) {
return Math.max(0, 1f/(float)Math.pow(2, Math.abs(offset)));
}
}
}
and the third activity is the reflected image below each image.
public class ReflectionImage extends ImageView {
//是否为Reflection模式
private boolean mReflectionMode = true;
public ReflectionImage(Context context) {
super(context);
}
public ReflectionImage(Context context, AttributeSet attrs) {
super(context, attrs);
//取得原始图片的bitmap并重画
Bitmap originalImage = ((BitmapDrawable)this.getDrawable()).getBitmap();
DoReflection(originalImage);
}
public ReflectionImage(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Bitmap originalImage = ((BitmapDrawable)this.getDrawable()).getBitmap();
DoReflection(originalImage);
}
public void setReflectionMode(boolean isRef) {
mReflectionMode = isRef;
}
public boolean getReflectionMode() {
return mReflectionMode;
}
//偷懒了,只重写了setImageResource,和构造函数里面干了同样的事情
#Override
public void setImageResource(int resId) {
Bitmap originalImage = BitmapFactory.decodeResource(
getResources(), resId);
DoReflection(originalImage);
//super.setImageResource(resId);
}
private void DoReflection(Bitmap originalImage) {
final int reflectionGap = 4; //原始图片和反射图片中间的间距
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//反转
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//reflectionImage就是下面透明的那部分,可以设置它的高度为原始的3/4,这样效果会更好些
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
0, width, height, matrix, false);
//创建一个新的bitmap,高度为原来的两倍
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height), Config.ARGB_8888);
Canvas canvasRef = new Canvas(bitmapWithReflection);
//先画原始的图片
canvasRef.drawBitmap(originalImage, 0, 0, null);
//画间距
Paint deafaultPaint = new Paint();
canvasRef.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//画被反转以后的图片
canvasRef.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
// 创建一个渐变的蒙版放在下面被反转的图片上面
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0,
originalImage.getHeight(), 0, bitmapWithReflection.getHeight()
+ reflectionGap, 0x80ffffff, 0x00ffffff, TileMode.CLAMP);
// Set the paint to use this shader (linear gradient)
paint.setShader(shader);
// Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// Draw a rectangle using the paint with our linear gradient
canvasRef.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);
//调用ImageView中的setImageBitmap
this.setImageBitmap(bitmapWithReflection);
}
}
I want to make it appears like a gallery that is all images must have four dp margin and appear like the center image without any effects. Can someone please help me to achieve this.
Please try this:
private int mMaxRotationAngle = 0;
and
CoverFlow cf = new CoverFlow(this);
cf.setSpacing(-30);
in the CoverFlowActivity
From what I understood, you need a ViewPager:
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
NOTE: add the support library to support API till level 4 ;-)
Some usefull code: http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html
setContentView(R.layout.main);
mContainer = (PagerContainer) findViewById(R.id.pager_container);
ViewPager pager = mContainer.getViewPager();
PagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
//Necessary or the pager will only have one extra page to show
// make this at least however many pages you can see
pager.setOffscreenPageLimit(adapter.getCount());
//A little space between pages
pager.setPageMargin(15);
//If hardware acceleration is enabled, you should also remove
// clipping on the pager for its children.
pager.setClipChildren(false);
Adding padding and margin should be trivial ;-)

Categories

Resources