Does someone know what might be the source of an error which occurs randomly, without being able to identify a clear scenario?
java.lang.RuntimeException: Unable to start activity ComponentInfo {com.xxx/com.xxx.activities.SplashActivity}: android.view.InflateException: Binary XML file line #38: Error inflating class <unknown>
A fragment from the layout is posted below:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/dark_gray">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/xl" />
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:padding="10dp"
android:text="xxx"
android:textColor="#FFF"
android:textSize="24sp" />
</LinearLayout>
<RelativeLayout
android:id="#+id/welcomeContent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xxx.utils.ParallaxViewPager // line 38
android:id="#+id/parallaxviewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg_parallax">
The class com.xxx.utils.ParallaxViewPager exists and I can catch the error very randomly.
public class ParallaxViewPager extends ViewPager {
public static final int FIT_WIDTH = 0;
public static final int FIT_HEIGHT = 1;
public static final float OVERLAP_FULL = 1f;
public static final float OVERLAP_HALF = 0.5f;
public static final float OVERLAP_QUARTER = 0.25f;
private static final float CORRECTION_PERCENTAGE = 0.01f;
public Bitmap bitmap;
private Rect source, destination;
private int scaleType;
private int chunkWidth;
private int projectedWidth;
private float overlap;
private OnPageChangeListener secondOnPageChangeListener;
private TextView[] dots;
private ScrollerCustomDuration mScroller = null;
float mStartDragX;
OnSwipeOutListener mListener;
public ParallaxViewPager(Context context) {
super(context);
init();
}
public ParallaxViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
postInitViewPager();
}
public void setScrollDurationFactor(double scrollFactor) {
mScroller.setScrollDurationFactor(scrollFactor);
}
public void setOnSwipeOutListener(OnSwipeOutListener listener) {
mListener = listener;
}
private void init() {
source = new Rect();
destination = new Rect();
scaleType = FIT_HEIGHT;
overlap = OVERLAP_HALF;
setOnPageChangeListener(new OnPageChangeListener() {
#Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position < 3) {
if (bitmap != null) {
source.left = (int) Math.floor((position + positionOffset - CORRECTION_PERCENTAGE) * chunkWidth);
source.right = (int) Math.ceil((position + positionOffset + CORRECTION_PERCENTAGE) * chunkWidth + projectedWidth);
destination.left = (int) Math.floor((position + positionOffset - CORRECTION_PERCENTAGE) * getWidth());
destination.right = (int) Math.ceil((position + positionOffset + 1 + CORRECTION_PERCENTAGE) * getWidth());
invalidate();
}
if (secondOnPageChangeListener != null) {
secondOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
}
#Override public void onPageSelected(int position) {
if (secondOnPageChangeListener != null) {
secondOnPageChangeListener.onPageSelected(position);
}
for (int i = 0; i < 3; i++) {
dots[i].setTextColor(getResources().getColor(android.R.color.darker_gray));
}
if (position < 3) {
dots[position].setTextColor(getResources().getColor(R.color.tw__solid_white));
}
if (position == 3) {
mListener.onSwipeOutAtEnd();
}
}
#Override public void onPageScrollStateChanged(int state) {
if (secondOnPageChangeListener != null) {
secondOnPageChangeListener.onPageScrollStateChanged(state);
}
}
});
}
#Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
destination.top = 0;
destination.bottom = h;
if (getAdapter() != null && bitmap != null)
calculateParallaxParameters();
}
private void calculateParallaxParameters() {
if (bitmap.getWidth() < getWidth() && bitmap.getWidth() < bitmap.getHeight() && scaleType == FIT_HEIGHT) {
Log.w(ParallaxViewPager.class.getName(), "Invalid bitmap bounds for the current device, parallax effect will not work.");
}
final float ratio = (float) getHeight() / bitmap.getHeight();
if (ratio != 1) {
switch (scaleType) {
case FIT_WIDTH:
source.top = (int) ((bitmap.getHeight() - bitmap.getHeight() / ratio) / 2);
source.bottom = bitmap.getHeight() - source.top;
chunkWidth = (int) Math.ceil((float) bitmap.getWidth() / (float) getAdapter().getCount());
projectedWidth = chunkWidth;
break;
case FIT_HEIGHT:
default:
source.top = 0;
source.bottom = bitmap.getHeight();
projectedWidth = (int) Math.ceil(getWidth() / ratio);
chunkWidth = (int) Math.ceil((bitmap.getWidth() - projectedWidth) / (float) getAdapter().getCount() * overlap);
break;
}
}
}
/**
* Sets the background from a resource file.
*
* #param resid
*/
#Override public void setBackgroundResource(int resid) {
bitmap = BitmapFactory.decodeResource(getResources(), resid);
}
/**
* Sets the background from a Drawable.
*
* #param background
*/
#Override public void setBackground(Drawable background) {
bitmap = ((BitmapDrawable) background).getBitmap();
}
/**
* Deprecated.
* Sets the background from a Drawable.
*
* #param background
*/
#Override public void setBackgroundDrawable(Drawable background) {
bitmap = ((BitmapDrawable) background).getBitmap();
}
/**
* Sets the background from a bitmap.
*
* #param bitmap
* #return The ParallaxViewPager object itself.
*/
public ParallaxViewPager setBackground(Bitmap bitmap) {
this.bitmap = bitmap;
return this;
}
/**
* Sets how the view should scale the background. The available choices are:
* <ul>
* <li>FIT_HEIGHT - the height of the image is resized to matched the height of the View, also stretching the width to keep the aspect ratio. The non-visible part of the bitmap is divided into equal parts, each of them sliding in at the proper position.</li>
* <li>FIT_WIDTH - the width of the background image is divided into equal chunks, each taking up the whole width of the screen.</li>
* </ul>
*
* #param scaleType
* #return
*/
public ParallaxViewPager setScaleType(final int scaleType) {
if (scaleType != FIT_WIDTH && scaleType != FIT_HEIGHT)
throw new IllegalArgumentException("Illegal argument: scaleType must be FIT_WIDTH or FIT_HEIGHT");
this.scaleType = scaleType;
return this;
}
/**
* Sets the amount of overlapping with the setOverlapPercentage(final float percentage) method. This is a number between 0 and 1, the smaller it is, the slower is the background scrolling.
*
* #param percentage
* #return The ParallaxViewPager object itself.
*/
public ParallaxViewPager setOverlapPercentage(final float percentage) {
if (percentage <= 0 || percentage >= 1)
throw new IllegalArgumentException("Illegal argument: percentage must be between 0 and 1");
overlap = percentage;
return this;
}
/**
* Recalculates the parameters of the parallax effect, useful after changes in runtime.
*
* #return The ParallaxViewPager object itself.
*/
public ParallaxViewPager invalidateParallaxParameters() {
calculateParallaxParameters();
return this;
}
#Override protected void onDraw(Canvas canvas) {
if (bitmap != null)
canvas.drawBitmap(bitmap, source, destination, null);
}
public void addOnPageChangeListener(OnPageChangeListener listener) {
secondOnPageChangeListener = listener;
}
public TextView[] getDots() {
return dots;
}
public void setDots(TextView[] dots) {
this.dots = dots;
}
private void postInitViewPager() {
try {
Class<?> viewpager = ViewPager.class;
Field scroller = viewpager.getDeclaredField("mScroller");
scroller.setAccessible(true);
Field interpolator = viewpager.getDeclaredField("sInterpolator");
interpolator.setAccessible(true);
mScroller = new ScrollerCustomDuration(getContext(),
(Interpolator) interpolator.get(null));
scroller.set(this, mScroller);
} catch (Exception e) {
Log.e("MyPager", e.getMessage());
}
}
class ScrollerCustomDuration extends Scroller {
private double mScrollFactor = 2;
public ScrollerCustomDuration(Context context) {
super(context);
}
public ScrollerCustomDuration(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ScrollerCustomDuration(Context context,
Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
/**
* Set the factor by which the duration will change
*/
public void setScrollDurationFactor(double scrollFactor) {
mScrollFactor = scrollFactor;
}
#Override
public void startScroll(int startX, int startY, int dx, int dy,
int duration) {
super.startScroll(startX, startY, dx, dy,
(int) (duration * mScrollFactor));
}
}
public interface OnSwipeOutListener {
public void onSwipeOutAtEnd();
}}
Thanks for your input.
Related
I'm using a custom view to get ripple effect for the pre-lollipop devices. But I also need to customize the container shape like a curved shape.I want to be the button like this.
As you can see in the second and third buttons when we tap the view the ripple effect animation goes outside of the container view. So how to resolve this?
Please note that I want this ripple effect for the Kitkat version with the ability to change the ripple color. So is this possible?
Here is my custom view which is used for the ripple effect
public class MyRippleView extends FrameLayout {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 400;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
#Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public MyRippleView(Context context) {
super(context);
}
public MyRippleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyRippleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* Method that initializes all fields and sets listeners
*
* #param context Context used to create this view
* #param attrs Attribute used to initialize fields
*/
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null) onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
* Launch Ripple animation for the current view with a MotionEvent
*
* #param event MotionEvent registered by the Ripple gesture listener
*/
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
* Launch Ripple animation for the current view centered at x and y position
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
* Create Ripple animation centered at x, y
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
this.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
/**
* Send a click event if parent view is a Listview instance
*
* #param isLongClick Is the event a long click ?
*/
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
* Set Ripple color, default is #FFFFFF
*
* #param rippleColor New color resource
*/
#ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType()
{
return RippleType.values()[rippleType];
}
/**
* Set Ripple type, default is RippleType.SIMPLE
*
* #param rippleType New Ripple type for next animation
*/
public void setRippleType(final RippleType rippleType)
{
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered()
{
return isCentered;
}
/**
* Set if ripple animation has to be centered in its parent view or not, default is False
*
* #param isCentered
*/
public void setCentered(final Boolean isCentered)
{
this.isCentered = isCentered;
}
public int getRipplePadding()
{
return ripplePadding;
}
/**
* Set Ripple padding if you want to avoid some graphic glitch
*
* #param ripplePadding New Ripple padding in pixel, default is 0px
*/
public void setRipplePadding(int ripplePadding)
{
this.ripplePadding = ripplePadding;
}
public Boolean isZooming()
{
return hasToZoom;
}
/**
* At the end of Ripple effect, the child views has to zoom
*
* #param hasToZoom Do the child views have to zoom ? default is False
*/
public void setZooming(Boolean hasToZoom)
{
this.hasToZoom = hasToZoom;
}
public float getZoomScale()
{
return zoomScale;
}
/**
* Scale of the end animation
*
* #param zoomScale Value of scale animation, default is 1.03f
*/
public void setZoomScale(float zoomScale)
{
this.zoomScale = zoomScale;
}
public int getZoomDuration()
{
return zoomDuration;
}
/**
* Duration of the ending animation in ms
*
* #param zoomDuration Duration, default is 200ms
*/
public void setZoomDuration(int zoomDuration)
{
this.zoomDuration = zoomDuration;
}
public int getRippleDuration()
{
return rippleDuration;
}
/**
* Duration of the Ripple animation in ms
*
* #param rippleDuration Duration, default is 400ms
*/
public void setRippleDuration(int rippleDuration)
{
this.rippleDuration = rippleDuration;
}
public int getFrameRate()
{
return frameRate;
}
/**
* Set framerate for Ripple animation
*
* #param frameRate New framerate value, default is 10
*/
public void setFrameRate(int frameRate)
{
this.frameRate = frameRate;
}
public int getRippleAlpha()
{
return rippleAlpha;
}
/**
* Set alpha for ripple effect color
*
* #param rippleAlpha Alpha value between 0 and 255, default is 90
*/
public void setRippleAlpha(int rippleAlpha)
{
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
/**
* Defines a callback called at the end of the Ripple effect
*/
public interface OnRippleCompleteListener {
void onComplete(MyRippleView rippleView);
}
public enum RippleType {
SIMPLE(0),
DOUBLE(1),
RECTANGLE(2);
int type;
RippleType(int type)
{
this.type = type;
}
}
}
In the layout XML file
<FrameLayout
android:background="#drawable/curved_button"
android:layout_width="match_parent"
android:layout_height="50dp">
<com.package.MyRippleView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rv_color="#color/colorAccent"
rv_centered="true">
</com.package.MyRippleView>
</FrameLayout>
Curved Shape
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item >
<shape android:shape="rectangle" >
<corners android:radius="40dip" />
<stroke android:width="1dp" android:color="#FF9A00" />
</shape>
</item>
It's possible. The easiest way is to use Carbon which does such things just like that. I was able to recreate your button using only xml and run it on Gingerbread.
<carbon.widget.Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rounded with ripple"
android:textColor="#color/carbon_amber_700"
app:carbon_cornerRadius="100dp"
app:carbon_backgroundTint="#color/carbon_white"
app:carbon_rippleColor="#40ff0000"
app:carbon_stroke="#color/carbon_amber_700"
app:carbon_strokeWidth="2dp" />
The downside is that Carbon is huge and you may not want to include it just for that one button.
If you wish to do that by yourself, you should use a path and a PorterDuff mode to clip your button to a rounded rect.
private float cornerRadius;
private Path cornersMask;
private static PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private void initCorners() {
cornersMask = new Path();
cornersMask.addRoundRect(new RectF(0, 0, getWidth(), getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
cornersMask.setFillType(Path.FillType.INVERSE_WINDING);
}
#Override
public void draw(#NonNull Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.draw(canvas);
paint.setXfermode(pdMode);
canvas.drawPath(cornersMask, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
And you should probably use ViewOutlineProvider on Lollipop to use native stuff where possible.
I want a custom view that moves depending on the accelerometers and creating the parallax effect.
Now I have the custom view listening the accelerometers values but, how I can use these values to move the view properly?
code:
public class ParallaxView extends AppCompatImageView
implements SensorEventListener {
private static final int SENSOR_DELAY = SensorManager.SENSOR_DELAY_FASTEST;
//...
public ParallaxView(Context context) {
super(context);
}
public ParallaxView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParallaxView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void init() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(WINDOW_SERVICE);
mDisplay = windowManager.getDefaultDisplay();
mSensorManager = (SensorManager) getContext().getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
public void setNewPosition(
#Nullable Float sensorX,
#Nullable Float sensorY) {
// ???
}
//...
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
setNewPosition(event.values[0], event.values[1]);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
public void registerSensorListener() {
mSensorManager.registerListener(this, mAccelerometer, SENSOR_DELAY);
}
public void unregisterSensorListener() {
mSensorManager.unregisterListener(this);
}
}
Use this view in the Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
mParallaxView.init();
}
#Override
protected void onResume() {
mParallaxView.registerSensorListener();
super.onResume();
}
#Override
protected void onPause() {
mParallaxView.unregisterSensorListener();
super.onPause();
}
Thanks in advance
Finally I created my custom view for get what I want.
Here the repository: https://github.com/GVMarc/ParallaxView
Here the code:
public class ParallaxView extends AppCompatImageView implements SensorEventListener {
private static final int DEFAULT_SENSOR_DELAY = SensorManager.SENSOR_DELAY_FASTEST;
public static final int DEFAULT_MOVEMENT_MULTIPLIER = 3;
public static final int DEFAULT_MIN_MOVED_PIXELS = 1;
private static final float DEFAULT_MIN_SENSIBILITY = 0;
private float mMovementMultiplier = DEFAULT_MOVEMENT_MULTIPLIER;
private int mSensorDelay = DEFAULT_SENSOR_DELAY;
private int mMinMovedPixelsToUpdate = DEFAULT_MIN_MOVED_PIXELS;
private float mMinSensibility = DEFAULT_MIN_SENSIBILITY;
private float mSensorX;
private float mSensorY;
private Float mFirstSensorX;
private Float mFirstSensorY;
private Float mPreviousSensorX;
private Float mPreviousSensorY;
private float mTranslationX = 0;
private float mTranslationY = 0;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
public enum SensorDelay {
FASTEST,
GAME,
UI,
NORMAL
}
public ParallaxView(Context context) {
super(context);
}
public ParallaxView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParallaxView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void init() {
mSensorManager = (SensorManager) getContext().getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
private void setNewPosition() {
int destinyX = (int) ((mFirstSensorX - mSensorX) * mMovementMultiplier);
int destinyY = (int) ((mFirstSensorY - mSensorY) * mMovementMultiplier);
calculateTranslationX(destinyX);
calculateTranslationY(destinyY);
}
private void calculateTranslationX(int destinyX) {
if (mTranslationX + mMinMovedPixelsToUpdate < destinyX)
mTranslationX++;
else if (mTranslationX - mMinMovedPixelsToUpdate > destinyX)
mTranslationX--;
}
private void calculateTranslationY(int destinyY) {
if (mTranslationY + mMinMovedPixelsToUpdate < destinyY)
mTranslationY++;
else if (mTranslationY - mMinMovedPixelsToUpdate > destinyY)
mTranslationY--;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setTranslationX(mTranslationX);
setTranslationY(mTranslationY);
invalidate();
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mSensorX = event.values[0];
mSensorY = -event.values[1];
manageSensorValues();
}
}
private void manageSensorValues() {
if (mFirstSensorX == null)
setFirstSensorValues();
if (mPreviousSensorX == null || isSensorValuesMovedEnough()) {
setNewPosition();
setPreviousSensorValues();
}
}
private void setFirstSensorValues() {
mFirstSensorX = mSensorX;
mFirstSensorY = mSensorY;
}
private void setPreviousSensorValues() {
mPreviousSensorX = mSensorX;
mPreviousSensorY = mSensorY;
}
private boolean isSensorValuesMovedEnough() {
return mSensorX > mPreviousSensorX + mMinSensibility ||
mSensorX < mPreviousSensorX - mMinSensibility ||
mSensorY > mPreviousSensorY + mMinSensibility ||
mSensorY < mPreviousSensorX - mMinSensibility;
}
public void registerSensorListener() {
mSensorManager.registerListener(this, mAccelerometer, mSensorDelay);
}
public void registerSensorListener(SensorDelay sensorDelay) {
switch (sensorDelay) {
case FASTEST:
mSensorDelay = SensorManager.SENSOR_DELAY_FASTEST;
break;
case GAME:
mSensorDelay = SensorManager.SENSOR_DELAY_GAME;
break;
case UI:
mSensorDelay = SensorManager.SENSOR_DELAY_UI;
break;
case NORMAL:
mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
break;
}
registerSensorListener();
}
public void unregisterSensorListener() {
mSensorManager.unregisterListener(this);
}
public void setMovementMultiplier(float multiplier) {
mMovementMultiplier = multiplier;
}
public void setMinimumMovedPixelsToUpdate(int minMovedPixelsToUpdate) {
mMinMovedPixelsToUpdate = minMovedPixelsToUpdate;
}
public void setMinimumSensibility(int minSensibility) {
mMinSensibility = minSensibility;
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
Here is a ParallaxImageView class I have used before:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Matrix;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.widget.ImageView;
import yourpackagename.R;
/**
* I did not write this, I just cant remember where I got it from and thought it could be useful for others
*/
public class ParallaxImageView extends ImageView implements SensorEventListener {
private static final String TAG = ParallaxImageView.class.getName();
/**
* If the x and y axis' intensities are scaled to the image's aspect ratio (true) or
* equal to the smaller of the axis' intensities (false). If true, the image will be able to
* translate up to it's view bounds, independent of aspect ratio. If not true,
* the image will limit it's translation equally so that motion in either axis results
* in proportional translation.
*/
private boolean mScaledIntensities = false;
/**
* The intensity of the parallax effect, giving the perspective of depth.
*/
private float mParallaxIntensity = 1.15f;
/**
* The maximum percentage of offset translation that the image can move for each
* sensor input. Set to a negative number to disable.
*/
private float mMaximumJump = .1f;
// Instance variables used during matrix manipulation.
private SensorInterpreter mSensorInterpreter;
private SensorManager mSensorManager;
private Matrix mTranslationMatrix;
private float mXTranslation;
private float mYTranslation;
private float mXOffset;
private float mYOffset;
public ParallaxImageView(Context context) {
this(context, null);
}
public ParallaxImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ParallaxImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Instantiate future objects
mTranslationMatrix = new Matrix();
mSensorInterpreter = new SensorInterpreter();
// Sets scale type
setScaleType(ScaleType.MATRIX);
// Set available attributes
if (attrs != null) {
final TypedArray customAttrs = context.obtainStyledAttributes(attrs, R.styleable.ParallaxImageView);
if (customAttrs != null) {
if (customAttrs.hasValue(R.styleable.ParallaxImageView_intensity))
setParallaxIntensity(customAttrs.getFloat(R.styleable.ParallaxImageView_intensity, mParallaxIntensity));
if (customAttrs.hasValue(R.styleable.ParallaxImageView_scaledIntensity))
setScaledIntensities(customAttrs.getBoolean(R.styleable.ParallaxImageView_scaledIntensity, mScaledIntensities));
if (customAttrs.hasValue(R.styleable.ParallaxImageView_tiltSensitivity))
setTiltSensitivity(customAttrs.getFloat(R.styleable.ParallaxImageView_tiltSensitivity,
mSensorInterpreter.getTiltSensitivity()));
if (customAttrs.hasValue(R.styleable.ParallaxImageView_forwardTiltOffset))
setForwardTiltOffset(customAttrs.getFloat(R.styleable.ParallaxImageView_forwardTiltOffset,
mSensorInterpreter.getForwardTiltOffset()));
customAttrs.recycle();
}
}
// Configure matrix as early as possible by posting to MessageQueue
post(new Runnable() {
#Override
public void run() {
configureMatrix();
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
configureMatrix();
}
/**
* Sets the intensity of the parallax effect. The stronger the effect, the more distance
* the image will have to move around.
*
* #param parallaxIntensity the new intensity
*/
public void setParallaxIntensity(float parallaxIntensity) {
if (parallaxIntensity < 1)
throw new IllegalArgumentException("Parallax effect must have a intensity of 1.0 or greater");
mParallaxIntensity = parallaxIntensity;
configureMatrix();
}
/**
* Sets the parallax tilt sensitivity for the image view. The stronger the sensitivity,
* the more a given tilt will adjust the image and the smaller needed tilt to reach the
* image bounds.
*
* #param sensitivity the new tilt sensitivity
*/
public void setTiltSensitivity(float sensitivity) {
mSensorInterpreter.setTiltSensitivity(sensitivity);
}
/**
* Sets the forward tilt offset dimension, allowing for the image to be
* centered while the phone is "naturally" tilted forwards.
*
* #param forwardTiltOffset the new tilt forward adjustment
*/
public void setForwardTiltOffset(float forwardTiltOffset) {
if (Math.abs(forwardTiltOffset) > 1)
throw new IllegalArgumentException("Parallax forward tilt offset must be less than or equal to 1.0");
mSensorInterpreter.setForwardTiltOffset(forwardTiltOffset);
}
/**
* Sets whether translation should be limited to the image's bounds or should be limited
* to the smaller of the two axis' translation limits.
*
* #param scaledIntensities the scaledIntensities flag
*/
public void setScaledIntensities(boolean scaledIntensities) {
mScaledIntensities = scaledIntensities;
}
/**
* Sets the maximum percentage of the image that image matrix is allowed to translate
* for each sensor reading.
*
* #param maximumJump the new maximum jump
*/
public void setMaximumJump(float maximumJump) {
mMaximumJump = maximumJump;
}
/**
* Sets the image view's translation coordinates. These values must be between -1 and 1,
* representing the transaction percentage from the center.
*
* #param x the horizontal translation
* #param y the vertical translation
*/
private void setTranslate(float x, float y) {
if (Math.abs(x) > 1 || Math.abs(y) > 1)
throw new IllegalArgumentException("Parallax effect cannot translate more than 100% of its off-screen size");
float xScale, yScale;
if (mScaledIntensities) {
// Set both scales to their offset values
xScale = mXOffset;
yScale = mYOffset;
} else {
// Set both scales to the max offset (should be negative, so smaller absolute value)
xScale = Math.max(mXOffset, mYOffset);
yScale = Math.max(mXOffset, mYOffset);
}
// Make sure below maximum jump limit
if (mMaximumJump > 0) {
// Limit x jump
if (x - mXTranslation / xScale > mMaximumJump) {
x = mXTranslation / xScale + mMaximumJump;
} else if (x - mXTranslation / xScale < -mMaximumJump) {
x = mXTranslation / xScale - mMaximumJump;
}
// Limit y jump
if (y - mYTranslation / yScale > mMaximumJump) {
y = mYTranslation / yScale + mMaximumJump;
} else if (y - mYTranslation / yScale < -mMaximumJump) {
y = mYTranslation / yScale - mMaximumJump;
}
}
mXTranslation = x * xScale;
mYTranslation = y * yScale;
configureMatrix();
}
/**
* Configures the ImageView's imageMatrix to allow for movement of the
* source image.
*/
private void configureMatrix() {
if (getDrawable() == null || getWidth() == 0 || getHeight() == 0) return;
int dWidth = getDrawable().getIntrinsicWidth();
int dHeight = getDrawable().getIntrinsicHeight();
int vWidth = getWidth();
int vHeight = getHeight();
float scale;
float dx, dy;
if (dWidth * vHeight > vWidth * dHeight) {
scale = (float) vHeight / (float) dHeight;
mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f;
mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f;
} else {
scale = (float) vWidth / (float) dWidth;
mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f;
mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f;
}
dx = mXOffset + mXTranslation;
dy = mYOffset + mYTranslation;
mTranslationMatrix.set(getImageMatrix());
mTranslationMatrix.setScale(mParallaxIntensity * scale, mParallaxIntensity * scale);
mTranslationMatrix.postTranslate(dx, dy);
setImageMatrix(mTranslationMatrix);
}
/**
* Registers a sensor manager with the parallax ImageView. Should be called in onResume
* from an Activity or Fragment.
*
*/
#SuppressWarnings("deprecation")
public void registerSensorManager() {
if (getContext() == null || mSensorManager != null) return;
// Acquires a sensor manager
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager != null) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_FASTEST);
}
}
/**
* Unregisters the ParallaxImageView's SensorManager. Should be called in onPause from
* an Activity or Fragment to avoid continuing sensor usage.
*/
public void unregisterSensorManager() {
unregisterSensorManager(false);
}
/**
* Unregisters the ParallaxImageView's SensorManager. Should be called in onPause from
* an Activity or Fragment to avoid continuing sensor usage.
* #param resetTranslation if the image translation should be reset to the origin
*/
public void unregisterSensorManager(boolean resetTranslation) {
if (mSensorManager == null) return;
mSensorManager.unregisterListener(this);
mSensorManager = null;
if (resetTranslation) {
setTranslate(0, 0);
}
}
#Override
public void onSensorChanged(SensorEvent event) {
final float [] vectors = mSensorInterpreter.interpretSensorEvent(getContext(), event);
// Return if interpretation of data failed
if (vectors == null) return;
// Set translation on ImageView matrix
setTranslate(vectors[2], vectors[1]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
}
SensorInterpreter
import android.content.Context;
import android.hardware.SensorEvent;
import android.view.Surface;
import android.view.WindowManager;
/**
* I did not write this, I just cant remember where I got it from and thought it could be useful for others
*/
public class SensorInterpreter {
private static final String TAG = SensorInterpreter.class.getName();
private float[] mVectors;
private float mTiltSensitivity = 2.0f;
private float mForwardTiltOffset = 0.3f;
public SensorInterpreter() {
mVectors = new float[3];
}
public final float[] interpretSensorEvent(Context context, SensorEvent event) {
if (event == null || event.values.length < 3 || event.values[0] == 0
|| event.values[1] == 0 || event.values[2] == 0)
return null;
// Acquire rotation of screen
final int rotation = ((WindowManager) context
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
.getRotation();
// Adjust for forward tilt based on screen orientation
switch (rotation) {
case Surface.ROTATION_90:
mVectors[0] = event.values[0];
mVectors[1] = event.values[2];
mVectors[2] = -event.values[1];
break;
case Surface.ROTATION_180:
mVectors[0] = event.values[0];
mVectors[1] = event.values[1];
mVectors[2] = event.values[2];
break;
case Surface.ROTATION_270:
mVectors[0] = event.values[0];
mVectors[1] = -event.values[2];
mVectors[2] = event.values[1];
break;
default:
mVectors[0] = event.values[0];
mVectors[1] = -event.values[1];
mVectors[2] = -event.values[2];
break;
}
// Adjust roll for sensitivity differences based on pitch
// double tiltScale = 1/Math.cos(mVectors[1] * Math.PI/180);
// if (tiltScale > 12) tiltScale = 12;
// if (tiltScale < -12) tiltScale = -12;
// mVectors[2] *= tiltScale;
// Make roll and pitch percentages out of 1
mVectors[1] /= 90;
mVectors[2] /= 90;
// Add in forward tilt offset
mVectors[1] -= mForwardTiltOffset;
if (mVectors[1] < -1)
mVectors[1] += 2;
// Adjust for tilt sensitivity
mVectors[1] *= mTiltSensitivity;
mVectors[2] *= mTiltSensitivity;
// Clamp values to image bounds
if (mVectors[1] > 1)
mVectors[1] = 1f;
if (mVectors[1] < -1)
mVectors[1] = -1f;
if (mVectors[2] > 1)
mVectors[2] = 1f;
if (mVectors[2] < -1)
mVectors[2] = -1f;
return mVectors;
}
public float getForwardTiltOffset() {
return mForwardTiltOffset;
}
public void setForwardTiltOffset(float forwardTiltOffset) {
mForwardTiltOffset = forwardTiltOffset;
}
public float getTiltSensitivity() {
return mTiltSensitivity;
}
public void setTiltSensitivity(float tiltSensitivity) {
mTiltSensitivity = tiltSensitivity;
}
Add this to attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ParallaxImageView">
<attr name="intensity" format="float" />
<attr name="tiltSensitivity" format="float" />
<attr name="forwardTiltOffset" format="float" />
<attr name="scaledIntensity" format="boolean" />
</declare-styleable>
Add this to layout xml
<yourpackagenamehere.ParallaxImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
Add this to Activity class
private ParallaxImageView background;
background = (ParallaxImageView) findViewById(R.id.background);
background.setImageResource(R.drawable.main_back);
#Override
public void onResume() {
background.registerSensorManager();
super.onResume();
}
#Override
public void onPause() {
background.unregisterSensorManager();
super.onPause();
}
I'm new to Fresco library and trying to show circle Progressbar when loading image from uri into DraweeView. Now I'm using default progressbar as below:
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(getResources());
GenericDraweeHierarchy hierarchy = builder.setFadeDuration(fadeInTime).build();
hierarchy.setProgressBarImage(new ProgressBarDrawable());
thumbnailImageView.setHierarchy(hierarchy);
however, the ProgressBar is horizontal ProgressBar? Is there anyway to change it to circle?
After a lot of time to searching with Google, but I cant find any result. I decided to write my own CircleProgressDrawable. I just wanna share it for anyone who dont want to waste time as me.
public class CircleProgressDrawable extends ProgressBarDrawable {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mBackgroundColor = 0x80000000;
private int mColor = 0x800080FF;
private int mBarWidth = 20;
private int mLevel = 0;
private boolean mHideWhenZero = false;
private int radius = 50;
public CircleProgressDrawable() {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
}
public void setRadius(int radius) {
this.radius = radius;
}
/**
* Sets the progress bar color.
*/
public void setColor(int color) {
if (mColor != color) {
mColor = color;
invalidateSelf();
}
}
/**
* Gets the progress bar color.
*/
public int getColor() {
return mColor;
}
/**
* Sets the progress bar background color.
*/
public void setBackgroundColor(int backgroundColor) {
if (mBackgroundColor != backgroundColor) {
mBackgroundColor = backgroundColor;
invalidateSelf();
}
}
/**
* Gets the progress bar background color.
*/
public int getBackgroundColor() {
return mBackgroundColor;
}
/**
* Sets the progress bar width.
*/
public void setBarWidth(int barWidth) {
if (mBarWidth != barWidth) {
mBarWidth = barWidth;
invalidateSelf();
}
}
/**
* Gets the progress bar width.
*/
public int getBarWidth() {
return mBarWidth;
}
/**
* Sets whether the progress bar should be hidden when the progress is 0.
*/
public void setHideWhenZero(boolean hideWhenZero) {
mHideWhenZero = hideWhenZero;
}
/**
* Gets whether the progress bar should be hidden when the progress is 0.
*/
public boolean getHideWhenZero() {
return mHideWhenZero;
}
#Override
protected boolean onLevelChange(int level) {
mLevel = level;
invalidateSelf();
return true;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return DrawableUtils.getOpacityFromColor(mPaint.getColor());
}
#Override
public void draw(Canvas canvas) {
if (mHideWhenZero && mLevel == 0) {
return;
}
drawCircle(canvas, mBackgroundColor);
drawArc(canvas, mLevel, mColor);
}
private final int MAX_LEVEL = 10000;
private void drawArc(Canvas canvas, int level, int color) {
mPaint.setColor(color);
Rect bounds = getBounds();
// find center point
int xpos = bounds.left + bounds.width() / 2;
int ypos = bounds.bottom - bounds.height() / 2;
RectF rectF = new RectF(xpos - radius, ypos - radius, xpos + radius, ypos + radius);
float degree = (float) level / (float) MAX_LEVEL * 360;
canvas.drawArc(rectF, 270, degree, false, mPaint);
LogUtils.e("level: " + level + ", degree: " + degree);
}
private void drawCircle(Canvas canvas, int color) {
mPaint.setColor(color);
Rect bounds = getBounds();
int xpos = bounds.left + bounds.width() / 2;
int ypos = bounds.bottom - bounds.height() / 2;
canvas.drawCircle(xpos, ypos, radius, mPaint);
}
}
i have one fragment it contains one custom color seekbar and gridview . but when try to scroll seekbar its not moving . this fragment is of viewpager .
<com.example.app.ColorSeekBar
android:id="#+id/icon_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_margin="6dp"
android:visibility="visible" />
<GridView
android:id="#+id/fragment_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/icon_color"
android:layout_marginTop="5dp"
android:background="#color/grid_bg_new"
android:drawSelectorOnTop="true"
android:focusableInTouchMode="true"
android:horizontalSpacing="5dp"
android:numColumns="3"
android:verticalSpacing="5dp"></GridView>
Please help me how can i accomplish viewpager move as well as seekbar move. when i move seekbar pager must not move and when i swipe page then seekbar must not move.
i also put
icons_pager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
seekbar class
public ColorSeekBar(Context context) {
super(context);
init(context, null, 0, 0);
}
public ColorSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0, 0);
}
public ColorSeekBar(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ColorSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
applyStyle(context, attrs, defStyleAttr, defStyleRes);
}
public void applyStyle(int resId){
applyStyle(getContext(), null, 0, resId);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewWidth = widthMeasureSpec;
mViewHeight = heightMeasureSpec;
int speMode = MeasureSpec.getMode(heightMeasureSpec);
if(speMode == MeasureSpec.AT_MOST){
if(mIsShowAlphaBar){
setMeasuredDimension(mViewWidth,mThumbHeight * 2 + mBarHeight * 2 + mBarMargin);
}else{
setMeasuredDimension(mViewWidth,mThumbHeight + mBarHeight);
}
}
}
protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
mContext = context;
//get attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorSeekBar, defStyleAttr, defStyleRes);
int colorsId = a.getResourceId(R.styleable.ColorSeekBar_colors, 0);
mMaxValue = a.getInteger(R.styleable.ColorSeekBar_maxValue,100);
mColorBarValue = a.getInteger(R.styleable.ColorSeekBar_colorBarValue,0);
mAlphaBarValue = a.getInteger(R.styleable.ColorSeekBar_alphaBarValue,0);
mIsShowAlphaBar = a.getBoolean(R.styleable.ColorSeekBar_showAlphaBar, false);
mBackgroundColor = a.getColor(R.styleable.ColorSeekBar_bgColor, Color.TRANSPARENT);
mBarHeight = (int)a.getDimension(R.styleable.ColorSeekBar_barHeight,(float)dp2px(10));
mThumbHeight = (int)a.getDimension(R.styleable.ColorSeekBar_thumbHeight,(float)dp2px(30));
mBarMargin = (int)a.getDimension(R.styleable.ColorSeekBar_barMargin,(float)dp2px(5));
a.recycle();
if(colorsId != 0) mColors = getColorsById(colorsId);
setBackgroundColor(mBackgroundColor);
init();
//init color
pickColor(mColorBarValue);
setAlphaValue();
}
/**
*
* #param id
* #return
*/
private int[] getColorsById(int id){
if(isInEditMode()){
String[] s=mContext.getResources().getStringArray(id);
int[] colors = new int[s.length];
for (int j = 0; j < s.length; j++){
colors[j] = Color.parseColor(s[j]);
}
return colors;
}else{
TypedArray typedArray=mContext.getResources().obtainTypedArray(id);
int[] colors = new int[typedArray.length()];
for (int j = 0; j < typedArray.length(); j++){
colors[j] = typedArray.getColor(j,Color.BLACK);
}
typedArray.recycle();
return colors;
}
}
private void init() {
//init l r t b
realLeft = getPaddingLeft() + mPaddingSize;
realRight = getWidth() - getPaddingRight() - mPaddingSize;
realTop = getPaddingTop() + mPaddingSize;
realBottom = getHeight() - getPaddingBottom() - mPaddingSize;
//init size
mThumbRadius = mThumbHeight / 2;
mPaddingSize = (int)mThumbRadius;
mBarWidth = realRight - realLeft;
//init rect
mColorRect = new Rect(realLeft ,realTop,realRight,realTop + mBarHeight);
//init paint
mColorGradient = new LinearGradient(0, 0, mColorRect.width(), 0, mColors, null, Shader.TileMode.MIRROR);
mColorRectPaint = new Paint();
mColorRectPaint.setShader(mColorGradient);
mColorRectPaint.setAntiAlias(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mTransparentBitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_4444);
mTransparentBitmap.eraseColor(Color.TRANSPARENT);
}
public ColorSeekBar(Context context, float x1, float x2) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
init();
float colorPosition = (float) mColorBarValue / mMaxValue * mBarWidth;
Paint colorPaint = new Paint();
colorPaint.setAntiAlias(true);
colorPaint.setColor(pickColor(colorPosition));
int[] toAlpha=new int[]{Color.argb(255, mRed, mGreen, mBlue),Color.argb(0, mRed, mGreen, mBlue)};
//clear
canvas.drawBitmap(mTransparentBitmap,0,0,null);
//draw color bar
canvas.drawRect(mColorRect, mColorRectPaint);
//draw color bar thumb
float thumbX = colorPosition + realLeft;
float thumbY = mColorRect.top + mColorRect.height() / 2;
canvas.drawCircle(thumbX,thumbY , mBarHeight / 2 + 5, colorPaint);
//draw color bar thumb radial gradient shader
RadialGradient thumbShader = new RadialGradient(thumbX, thumbY, mThumbRadius, toAlpha, null, Shader.TileMode.MIRROR);
Paint thumbGradientPaint = new Paint();
thumbGradientPaint.setAntiAlias(true);
thumbGradientPaint.setShader(thumbShader);
canvas.drawCircle(thumbX, thumbY, mThumbHeight / 2, thumbGradientPaint);
if(mIsShowAlphaBar){
//init rect
int top = (int)(mThumbHeight+ mThumbRadius + mBarHeight + mBarMargin);
mAlphaRect = new Rect(realLeft,top,realRight,top + mBarHeight);
//draw alpha bar
Paint alphaBarPaint = new Paint();
alphaBarPaint.setAntiAlias(true);
LinearGradient alphaBarShader = new LinearGradient(0, 0, mAlphaRect.width(), 0, toAlpha, null, Shader.TileMode.MIRROR);
alphaBarPaint.setShader(alphaBarShader);
canvas.drawRect(mAlphaRect,alphaBarPaint);
//draw alpha bar thumb
float alphaPosition = (float) mAlphaBarValue / 255 * mBarWidth;
float alphaThumbX = alphaPosition + realLeft;
float alphaThumbY = mAlphaRect.top + mAlphaRect.height() / 2;
canvas.drawCircle(alphaThumbX, alphaThumbY, mBarHeight / 2 + 5, colorPaint);
//draw alpha bar thumb radial gradient shader
RadialGradient alphaThumbShader = new RadialGradient(alphaThumbX, alphaThumbY, mThumbRadius, toAlpha, null, Shader.TileMode.MIRROR);
Paint alphaThumbGradientPaint = new Paint();
alphaThumbGradientPaint.setAntiAlias(true);
alphaThumbGradientPaint.setShader(alphaThumbShader);
canvas.drawCircle(alphaThumbX,alphaThumbY, mThumbHeight/2, alphaThumbGradientPaint);
}
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if(isOnBar(mColorRect, x, y)){
mMovingColorBar = true;
}else if(mIsShowAlphaBar){
if(isOnBar(mAlphaRect, x, y)){
mMovingAlphaBar = true;
}
}
break;
case MotionEvent.ACTION_MOVE:
if(mMovingColorBar){
float value = (x - realLeft) / mBarWidth * mMaxValue;
mColorBarValue =(int)value ;
if (mColorBarValue < 0) mColorBarValue = 0;
if (mColorBarValue > mMaxValue) mColorBarValue = mMaxValue;
}else if(mIsShowAlphaBar){
if(mMovingAlphaBar){
float value = (x - realLeft) / mBarWidth * 255;
mAlphaBarValue = (int)value;
if (mAlphaBarValue < 0 ) mAlphaBarValue = 0;
if (mAlphaBarValue > 255 ) mAlphaBarValue = 255;
setAlphaValue();
}
}
if(mOnColorChangeLister != null && (mMovingAlphaBar || mMovingColorBar))
mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor());
invalidate();
break;
case MotionEvent.ACTION_UP:
mMovingColorBar = false;
mMovingAlphaBar = false;
break;
}
return true;
}
/**
*
* #param r
* #param x
* #param y
* #return whether MotionEvent is performing on bar or not
*/
private boolean isOnBar(Rect r, float x, float y){
if(r.left - mThumbRadius < x && x < r.right + mThumbRadius && r.top - mThumbRadius < y && y < r.bottom + mThumbRadius){
return true;
}else{
return false;
}
}
/**
*
* #param position
* #return color
*/
private int pickColor(float position) {
float unit = position / mBarWidth;
if (unit <= 0.0)
return mColors[0];
if (unit >= 1)
return mColors[mColors.length - 1];
float colorPosition = unit * (mColors.length - 1);
int i = (int)colorPosition;
colorPosition -= i;
c0 = mColors[i];
c1 = mColors[i+1];
// mAlpha = mix(Color.alpha(c0), Color.alpha(c1), colorPosition);
mRed = mix(Color.red(c0), Color.red(c1), colorPosition);
mGreen = mix(Color.green(c0), Color.green(c1), colorPosition);
mBlue = mix(Color.blue(c0), Color.blue(c1), colorPosition);
return Color.rgb( mRed, mGreen, mBlue);
}
/**
*
* #param start
* #param end
* #param position
* #return
*/
private int mix(int start, int end, float position) {
return start + Math.round(position * (end - start));
}
public int getColor() {
if(mIsShowAlphaBar) return Color.argb(mAlpha, mRed, mGreen, mBlue);
return Color.rgb( mRed, mGreen, mBlue);
}
public int getAlphaValue(){
return mAlpha;
}
/**
* #return color with alpha value
*/
public int getColorWithAlpha(){
return Color.argb(mAlpha, mRed, mGreen, mBlue);
}
public interface OnColorChangeListener {
/**
* #param colorBarValue between 0-maxValue
* #param alphaValue between 0-255
* #param color return the color contains alpha value whether showAlphaBar is true or without alpha value
*/
public void onColorChangeListener(int colorBarValue, int alphaValue, int color);
}
/**
* #param onColorChangeListener
*/
public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener){
this.mOnColorChangeLister = onColorChangeListener;
}
public int dp2px(float dpValue) {
final float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* Set colors by resource id. The resource's type must be color
* #param resId
*/
public void setColors(int resId){
setColors(getColorsById(resId));
}
public void setColors(int[] colors){
mColors = colors;
invalidate();
setAlphaValue();
if(mOnColorChangeLister != null)
mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor());
}
public int[] getColors(){
return mColors;
}
public boolean isShowAlphaBar(){
return mIsShowAlphaBar;
}
private void refreshLayoutParams(){
mThumbHeight = mThumbHeight < 2? 2:mThumbHeight;
mBarHeight = mBarHeight < 2? 2:mBarHeight;
int singleHeight = mThumbHeight + mBarHeight;
int doubleHeight = mThumbHeight *2 + mBarHeight * 2 + mBarMargin;
if(getLayoutParams().height == -2) {
if(mIsShowAlphaBar){
getLayoutParams().height = doubleHeight;
setLayoutParams(getLayoutParams());
}else{
getLayoutParams().height = singleHeight ;
setLayoutParams(getLayoutParams());
}
}else if (getLayoutParams().height >= 0){
if(mIsShowAlphaBar){
getLayoutParams().height = doubleHeight;
setLayoutParams(getLayoutParams());
}else{
getLayoutParams().height = singleHeight;
setLayoutParams(getLayoutParams());
}
}
}
public void setShowAlphaBar(boolean show){
mIsShowAlphaBar = show;
refreshLayoutParams();
invalidate();
if(mOnColorChangeLister != null)
mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor());
}
/**
* #param dp
*/
public void setBarHeight(float dp){
mBarHeight = dp2px(dp);
refreshLayoutParams();
invalidate();
}
/**
* #param px
*/
public void setBarHeightPx(int px){
mBarHeight = px;
refreshLayoutParams();
invalidate();
}
private void setAlphaValue(){
mAlpha = 255 - mAlphaBarValue;
}
public void setAlphaBarValue(int value) {
this.mAlphaBarValue = value;
setAlphaValue();
invalidate();
}
public int getMaxValue() {
return mMaxValue;
}
public void setMaxValue(int value) {
this.mMaxValue = value;
invalidate();
}
/**
* set margin between bars
* #param mBarMargin
*/
public void setBarMargin(float mBarMargin) {
this.mBarMargin = dp2px(mBarMargin);
refreshLayoutParams();
invalidate();
}
/**
* set margin between bars
* #param mBarMargin
*/
public void setBarMarginPx(int mBarMargin) {
this.mBarMargin = mBarMargin;
refreshLayoutParams();
invalidate();
}
/**
* Set the value of color bar, if out of bounds , it will be 0 or maxValue;
* #param value
*/
public void setColorBarValue(int value) {
this.mColorBarValue = value;
invalidate();
if(mOnColorChangeLister != null)
mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor());
}
/**
* set thumb's height by dpi
* #param dp
*/
public void setThumbHeight(float dp) {
this.mThumbHeight = dp2px(dp);
refreshLayoutParams();
invalidate();
}
/**
* set thumb's height by pixels
* #param px
*/
public void setThumbHeightPx(int px) {
this.mThumbHeight = px;
refreshLayoutParams();
invalidate();
}
public int getBarHeight() {
return mBarHeight;
}
public int getThumbHeight() {
return mThumbHeight;
}
public int getBarMargin() {
return mBarMargin;
}
public float getColorPosition() {
return mColorBarValue;
}
}
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 ;-)