I have tried to implement gradient view via native UI component RN approach (Android only for now). Everything seems fine except one: #ReactProp setters doesn't called and I don't even imagine why :(
Details:
- RN version: 0.60.4
Here is the code (ViewManager, View, RN component):
ViewManager:
package com.bastionpassmobile.splashgradient;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
/**
* View manager for splash background gradient view.
*/
public class SplashGradientViewManager extends SimpleViewManager<SplashGradientView> {
public static final String REACT_CLASS = "RCTSplashGradientView";
#Override
public String getName() {
return REACT_CLASS;
}
#Override
protected SplashGradientView createViewInstance(ThemedReactContext reactContext) {
return new SplashGradientView(reactContext);
}
}
View:
package com.bastionpassmobile.splashgradient;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
/**
* View serves as background gradient for splash screen.
*/
public class SplashGradientView extends ReactViewGroup {
float radius;
int[] colors;
int width;
int height;
public SplashGradientView(Context context) {
super(context);
radius = 0;
colors = new int[0];
width = 0;
height = 0;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (radius > 0) {
RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, radius, colors, null, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(radialGradient);
canvas.drawPaint(paint);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
/**
* Sets gradient radius.
* #param {SplashGradientView} view
* #param {float} value
*/
#ReactProp(name = "radius")
public void setRadius(SplashGradientView view, float value) {
radius = value;
view.invalidate();
}
/**
* Sets gradient colors.
* #param {SplashGradientView} view
* #param {ReadableArray} value
*/
#ReactProp(name = "colors")
public void setColors(SplashGradientView view, ReadableArray value) {
colors = new int[value.size()];
for (int i = 0; i < value.size(); i++) {
colors[i] = Color.parseColor(value.getString(i));
}
view.invalidate();
}
}
RN component:
import * as React from "react";
import { requireNativeComponent, StyleProp, ViewStyle } from "react-native";
interface CRadialGradientProps {
style?: StyleProp<ViewStyle>;
radius: number;
/**
* Each element of the array should be defined as hex color representation.
*/
colors: string[];
}
const RadialGradientView = requireNativeComponent("RCTSplashGradientView");
/**
* Represents radial gradient view.
*/
export class CRadialGradient extends React.Component<CRadialGradientProps> {
render() {
return (
<RadialGradientView {...this.props}/>
);
}
}
And render of course:
render() {
<CRadialGradient
radius={600}
colors={["#353946", "#1E212C"]}
/>
}
It's my mistake :) #ReactProp setters should be placed in ViewManager implementation instead of View. When I moved methods from View to ViewManager, it works fine.
Related
My question is the same as this question (which is not a duplicate of this question).
The only answer to that question does not work for me as, rather than changing the default hamburger icon to the left of the activity's title, it just adds an additional hamburger icon to the right of my activity's title.
So how do I actually get this:
I've been poking around at it all day, but have got nowhere.
I see that Toolbar has a setNavigationIcon(Drawable drawable) method. Ideally, I would like to use a layout (that contains the hamburger icon and the badge view) instead of a Drawable, but I'm not sure if/how this is achievable - or if there is a better way?
NB - This isn't a question about how to create the badge view. I have already created that and have implemented it on the nav menu items themselves. So I am now just needing to add a similar badge view to the default hamburger icon.
Since version 24.2.0 of the support library, the v7 version of ActionBarDrawerToggle has offered the setDrawerArrowDrawable() method as a means to customize the toggle icon. DrawerArrowDrawable is the class that provides that default icon, and it can be subclassed to alter it as needed.
As an example, the BadgeDrawerArrowDrawable class overrides the draw() method to add a basic red and white badge after the superclass draws itself. This allows the hamburger-arrow animation to be preserved underneath.
import android.content.Context;
import android.graphics.Color;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import java.util.Objects;
public class BadgeDrawerArrowDrawable extends DrawerArrowDrawable {
// Fraction of the drawable's intrinsic size we want the badge to be.
private static final float SIZE_FACTOR = .3f;
private static final float HALF_SIZE_FACTOR = SIZE_FACTOR / 2;
private Paint backgroundPaint;
private Paint textPaint;
private String text;
private boolean enabled = true;
public BadgeDrawerArrowDrawable(Context context) {
super(context);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.RED);
backgroundPaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(SIZE_FACTOR * getIntrinsicHeight());
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (!enabled) {
return;
}
final Rect bounds = getBounds();
final float x = (1 - HALF_SIZE_FACTOR) * bounds.width();
final float y = HALF_SIZE_FACTOR * bounds.height();
canvas.drawCircle(x, y, SIZE_FACTOR * bounds.width(), backgroundPaint);
if (text == null || text.length() == 0) {
return;
}
final Rect textBounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textBounds);
canvas.drawText(text, x, y + textBounds.height() / 2, textPaint);
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
invalidateSelf();
}
}
public boolean isEnabled() {
return enabled;
}
public void setText(String text) {
if (!Objects.equals(this.text, text)) {
this.text = text;
invalidateSelf();
}
}
public String getText() {
return text;
}
public void setBackgroundColor(int color) {
if (backgroundPaint.getColor() != color) {
backgroundPaint.setColor(color);
invalidateSelf();
}
}
public int getBackgroundColor() {
return backgroundPaint.getColor();
}
public void setTextColor(int color) {
if (textPaint.getColor() != color) {
textPaint.setColor(color);
invalidateSelf();
}
}
public int getTextColor() {
return textPaint.getColor();
}
}
An instance of this can be set on the toggle any time after it's instantiated, and the badge's properties set directly on the drawable as needed.
As the OP noted below, the Context used for the custom DrawerArrowDrawable should be obtained with ActionBar#getThemedContext() or Toolbar#getContext() to ensure the correct style values are used. For example:
private ActionBarDrawerToggle toggle;
private BadgeDrawerArrowDrawable badgeDrawable;
...
toggle = new ActionBarDrawerToggle(this, ...);
badgeDrawable = new BadgeDrawerArrowDrawable(getSupportActionBar().getThemedContext());
toggle.setDrawerArrowDrawable(badgeDrawable);
badgeDrawable.setText("1");
...
To simplify things a bit, it might be preferable to subclass ActionBarDrawerToggle as well, and handle everything through the toggle instance.
import android.app.Activity;
import android.content.Context;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BadgeDrawerToggle extends ActionBarDrawerToggle {
private BadgeDrawerArrowDrawable badgeDrawable;
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
Toolbar toolbar, int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, toolbar, openDrawerContentDescRes,
closeDrawerContentDescRes);
init(activity);
}
private void init(Activity activity) {
Context c = getThemedContext();
if (c == null) {
c = activity;
}
badgeDrawable = new BadgeDrawerArrowDrawable(c);
setDrawerArrowDrawable(badgeDrawable);
}
public void setBadgeEnabled(boolean enabled) {
badgeDrawable.setEnabled(enabled);
}
public boolean isBadgeEnabled() {
return badgeDrawable.isEnabled();
}
public void setBadgeText(String text) {
badgeDrawable.setText(text);
}
public String getBadgeText() {
return badgeDrawable.getText();
}
public void setBadgeColor(int color) {
badgeDrawable.setBackgroundColor(color);
}
public int getBadgeColor() {
return badgeDrawable.getBackgroundColor();
}
public void setBadgeTextColor(int color) {
badgeDrawable.setTextColor(color);
}
public int getBadgeTextColor() {
return badgeDrawable.getTextColor();
}
private Context getThemedContext() {
// Don't freak about the reflection. ActionBarDrawerToggle
// itself is already using reflection internally.
try {
Field mActivityImplField = ActionBarDrawerToggle.class
.getDeclaredField("mActivityImpl");
mActivityImplField.setAccessible(true);
Object mActivityImpl = mActivityImplField.get(this);
Method getActionBarThemedContextMethod = mActivityImpl.getClass()
.getDeclaredMethod("getActionBarThemedContext");
return (Context) getActionBarThemedContextMethod.invoke(mActivityImpl);
}
catch (Exception e) {
return null;
}
}
}
With this, the custom badge drawable will be set automatically, and everything toggle-related can be managed through a single object.
BadgeDrawerToggle is a drop-in replacement for ActionBarDrawerToggle, and its constructors are exactly the same.
private BadgeDrawerToggle badgeToggle;
...
badgeToggle = new BadgeDrawerToggle(this, ...);
badgeToggle.setBadgeText("1");
...
I'm trying to make small App. This App have Activity, Custom View Class, and service.
1) Activity ask service for new Data and redraw Custom view
2) Service is listning to Bluetooth device and parse data.
Everything was fine, but I noticed that App is slowing down after 40 mins working.
I made another project remove service and find that it slowing too! So problem is my Customview class, maybe i have memory leaks in service to... but i have problem with drawings 100%.
I found that i have some objects that i'm creating on onDraw() method.. i try to move all thise staff to onSizeChanged() - but get more lags.
And now i need help. I need some example with simple drawings that depends on device width and height (I think my method is wrong - i use proportions of my 'Design' to calculate demetions in px)
By the way i'm using animator which make animations more smooth))
public class Dynamics {
/**
* Used to compare floats, if the difference is smaller than this, they are
* considered equal
*/
private static final float TOLERANCE = 0.01f;
/** The position the dynamics should to be at */
private float targetPosition;
/** The current position of the dynamics */
private float position;
/** The current velocity of the dynamics */
private float velocity;
/** The time the last update happened */
private long lastTime;
/** The amount of springiness that the dynamics has */
private float springiness;
/** The damping that the dynamics has */
private double damping;
public Dynamics(float springiness, float dampingRatio) {
this.springiness = springiness;
this.damping = dampingRatio * 2 * Math.sqrt(springiness);
}
public void setPosition(float position, long now) {
this.position = position;
lastTime = now;
}
public void setVelocity(float velocity, long now) {
this.velocity = velocity;
lastTime = now;
}
public void setTargetPosition(float targetPosition, long now) {
this.targetPosition = targetPosition;
lastTime = now;
}
public void update(long now) {
float dt = Math.min(now - lastTime, 50) / 1000f;
float x = position - targetPosition;
double acceleration = -springiness * x - damping * velocity;
velocity += acceleration * dt;
position += velocity * dt;
lastTime = now;
}
public boolean isAtRest() {
final boolean standingStill = Math.abs(velocity) < TOLERANCE;
final boolean isAtTarget = (targetPosition - position) < TOLERANCE;
return standingStill && isAtTarget;
}
public float getPosition() {
return position;
}
public float getTargetPos() {
return targetPosition;
}
public float getVelocity() {
return velocity;
}
}
In my Custom view i have this to set new data:
public void SetData(int[] NewData2,float[]newDatapoints)
{
this.NewData=NewData2;
long now = AnimationUtils.currentAnimationTimeMillis();
if (datapoints == null || datapoints.length != newDatapoints.length) {
datapoints = new Dynamics[newDatapoints.length];
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i] = new Dynamics(70f, 0.50f);
datapoints[i].setPosition(newDatapoints[i], now);
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
invalidate();
} else {
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
removeCallbacks(animator);
post(animator);
}
LastData=NewData;
//redraw();
}
Thise is "code" of my custom view, after all changes it's look terible, so i cut 90% of it. And i make some test code insted:
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.AnimationUtils;
import java.io.IOException;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Random;
public class CustomDisplayView extends View {
//paint for drawing custom view
private Paint RectPaint = new Paint();
//Динамические данные float
private Dynamics[] datapoints;
//Динамические статические Int
private int[] NewData = new int[500];
//созадем новый объект квадрат
private RectF rectf= new RectF();
//Задаем массив динамических цветов
int[] CurColors= new int[100];
int[] TargetColors= new int[100];
public CustomDisplayView(Context context, AttributeSet attrs){
super(context, attrs);
//Установка парметров красок
RectPaint.setAntiAlias(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != 0 && h != 0) {
//create Bitmap here
}
}
/**
* Override the onDraw method to specify custom view appearance using canvas
*/
#Override
protected void onDraw(Canvas canvas) {
//Get Screen size
//int viewWidth=this.getMeasuredWidth();
// int viewHeight = this.getMeasuredHeight();
//Выводим код цвета
RectPaint.setColor(0xff000000);
RectPaint.setTextSize(40);
canvas.restore();
canvas.drawText("V: " + datapoints[1].getPosition(), 20, 60, RectPaint);
//int saveCount = canvas.save();
for(int a=0;a<1000;a++)
{
rectf.set(datapoints[a].getPosition(), datapoints[a + 1].getPosition(), datapoints[a].getPosition() + datapoints[a + 1].getPosition() / 10, datapoints[a + 1].getPosition() + datapoints[a + 1].getPosition() / 10);
RectPaint.setColor(0x88005020);
//RectPaint.setColor(CurColor[a]);
//canvas.rotate(datapoints[1].getPosition(), viewWidth/2, viewHeight/2);
canvas.drawRoundRect(rectf, 0, 0, RectPaint);
//canvas.restore();
}
//canvas.restoreToCount(saveCount);
/*
for(int a=0;a<999;a++)
{
CurColors[a]=progressiveColor(CurColors[a], TargetColors[a], 2);
if(CurColors[a]==TargetColors[a])
{
TargetColors[a]=randomColor();
}
}
*/
canvas.restore();
}
//Рандом колор
public static int randomColor(){
Random random = new Random();
int[] ColorParams= new int[4];
ColorParams[0]=random.nextInt(235)+20;
ColorParams[1]=random.nextInt(255);
ColorParams[2]=random.nextInt(255);
ColorParams[3]=random.nextInt(255);
return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);
}
//Интерполяция цвета
public static int progressiveColor(int CurColor,int TargetColor,int Step){
//Current color
int[] ColorParams= new int[4];
ColorParams[0]=(CurColor >> 24) & 0xFF;
ColorParams[1]=(CurColor >> 16) & 0xFF;
ColorParams[2]=(CurColor >> 8) & 0xFF;
ColorParams[3]=CurColor & 0xFF;
//TargetColor
int[] TargetColorParams= new int[4];
TargetColorParams[0]=(TargetColor >> 24) & 0xFF;
TargetColorParams[1]=(TargetColor >> 16) & 0xFF;
TargetColorParams[2]=(TargetColor >> 8) & 0xFF;
TargetColorParams[3]=TargetColor & 0xFF;
for(int i=0;i<4;i++)
{
if(ColorParams[i]<TargetColorParams[i])
{
ColorParams[i]+=Step;
if(ColorParams[i]>TargetColorParams[i])
{
ColorParams[i]=TargetColorParams[i];
}
}
else if(ColorParams[i]>TargetColorParams[i])
{
ColorParams[i]-=Step;
if(ColorParams[i]<TargetColorParams[i])
{
ColorParams[i]=TargetColorParams[i];
}
}
}
//int red = r - (int)((float)(r*255)/(float)all);
//int green = (int)((float)(g*255)/(float)all);
return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);
//return String.format("#%06X", (0xFFFFFF & Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3])));
//return " "+opacity+" "+red+" "+green+" "+blue;
}
//each custom attribute should have a get and set method
//this allows updating these properties in Java
//we call these in the main Activity class
/**
* Get the current text label color
* #return color as an int
*/
public int getLabelColor(){
return 1;
}
/**
* Set the label color
* #param newColor new color as an int
*/
public void setLabelColor(int newColor){
//update the instance variable
//labelCol=newColor;
//redraw the view
invalidate();
requestLayout();
}
public void redraw(){
//redraw the view
invalidate();
requestLayout();
}
public void SetData(int[] NewData2,float[]newDatapoints)
{
this.NewData=NewData2;
long now = AnimationUtils.currentAnimationTimeMillis();
if (datapoints == null || datapoints.length != newDatapoints.length) {
datapoints = new Dynamics[newDatapoints.length];
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i] = new Dynamics(70f, 0.50f);
datapoints[i].setPosition(newDatapoints[i], now);
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
invalidate();
} else {
for (int i = 0; i < newDatapoints.length; i++) {
datapoints[i].setTargetPosition(newDatapoints[i], now);
}
removeCallbacks(animator);
post(animator);
}
//redraw();
}
public int GetAction(float x,float y)
{
/*
if(x>(DicsCenterX-LineHalfSpeedZone) && x<(DicsCenterX+LineHalfSpeedZone) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
{
// private int SpeedZoneRadius2=0;
// private int PowerOutRadius=0;
//Смена режима
//начинаем смену размеру index / ms
ChangeVal(0,700);
return 1;
}
else if(x>(CofCantBGDrop*2) && x<(CofCantBGDrop*4) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
{
return 2;
}
else
{
return 0;
}
//return 0;
*/
return 1;
}
public static String fmt(double d)
{
double val = d/100;
String result;
if(val == (long) val)
result= String.format("%d",(long)d);
else
result= String.format("%s",d);
if(result.length()<2)
{
String result2=result;
result="0"+result2;
}
return result;
}
private Runnable animator = new Runnable() {
#Override
public void run() {
boolean needNewFrame = false;
long now = AnimationUtils.currentAnimationTimeMillis();
for (Dynamics dynamics : datapoints) {
dynamics.update(now);
if (!dynamics.isAtRest()) {
needNewFrame = true;
}
}
if (needNewFrame) {
postDelayed(this, 15);
}
invalidate();
}
};
}
I just want to understand where i need to declare scale values, where i need to calc real dimensions in px.. and et.c. to have no memory leaks..
If i remove color change and incrice number of Rects up to 1000 - i get lags.
All methods o any information how to debug memory leaks - you are wellcome!
You have to remove runnable for animation when view is detached.
if (needNewFrame) {
postDelayed(this, 15); <--- memory leak
}
Try like this.
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallback(your runnable);
}
Also refreshing every 15 milliseconds is very heavy.
I searched for and wide on the internet and I have yet to find an answer. I have an object called GameIcon which extends Sprite. Everything about it is okay except for the texture. Here is my code for the GameIcon class.
package com.xx4everPixelatedxx.gaterunner.sprites;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.xx4everPixelatedxx.gaterunner.GateRunner;
import javax.xml.soap.Text;
/**
* Created by Michael Jan on 8/17/2015.
*/
public class GameIcon extends Sprite {
private int vX = 3;
private int vY = 3;
private int r = 9;
private int rotation;
private Vector3 position;
private Texture texture;
public GameIcon(int x, int y) {
position = new Vector3(x, y, 0);
texture = new Texture(Gdx.files.internal("icon_players/icon1.png"));
setTexture(texture);
}
public void update() {
position.add(vX, vY, 0);
rotation = rotation + r;
rotation = rotation % 360;
setRotation(rotation);
setOriginCenter();
}
public void addPosition(int x, int y) {
position.add(x, y, 0);
setOriginCenter();
}
public void negateVelocityX() {
vX = -vX;
}
public void negateRotation() {
r = -r;
}
public Vector3 getPosition() {
return position;
}
public int getvY() {
return vY;
}
public void setvY(int vY) {
this.vY = vY;
}
public int getvX() {
return vX;
}
public void setvX(int vX) {
this.vX = vX;
}
}
Here is the icon1.png:
https://i.gyazo.com/1d52f5e58b227f08809f6c14ae4c94a4.png
Here is what I am getting:
https://i.gyazo.com/1881a9392955af34de5c55b9b8fac391.png
Note that the rotation of the image and the particles are intended. The problem is that the big square should be the texture (icon1.png), and I do not know how to fix this.
(Not enough reputation to post pictures)
I'm not familiar with LibGDX, but this may have something to do with the fact that you are maybe overwriting TextureRegion.texture. Could you try to user your parent class Sprite(Texture) constructor like this:
...
public class GameIcon extends Sprite {
private int vX = 3;
private int vY = 3;
private int r = 9;
private int rotation;
private Vector3 position;
//private Texture texture;
public GameIcon(int x, int y) {
super(new Texture(Gdx.files.internal("icon_players/icon1.png")))
position = new Vector3(x, y, 0);
}
...
As stated by Tenfour04 in comments, this method works because the parent's constructor applies the width and height of the texture, while setTexture() does not.
I'm currently using a BluringView XML Object as gotten from the BlurringView.java file (https://github.com/500px/500px-android-blur). It's basically just a custom view that blurs a sibling view that's beneath it. The problem I'm having is that I can't populate the BlurringView with other objects. Here's the code:
package com.fivehundredpx.android.blur;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.view.View;
/**
* A custom view for presenting a dynamically blurred version of another view's content.
* <p/>
* Use {#link #setBlurredView(android.view.View)} to set up the reference to the view to be blurred.
* After that, call {#link #invalidate()} to trigger blurring whenever necessary.
*/
public class BlurringView extends View {
public BlurringView(Context context) {
this(context, null);
}
public BlurringView(Context context, AttributeSet attrs) {
super(context, attrs);
final Resources res = getResources();
final int defaultBlurRadius = res.getInteger(R.integer.default_blur_radius);
final int defaultDownsampleFactor = res.getInteger(R.integer.default_downsample_factor);
final int defaultOverlayColor = res.getColor(R.color.default_overlay_color);
initializeRenderScript(context);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PxBlurringView);
setBlurRadius(a.getInt(R.styleable.PxBlurringView_blurRadius, defaultBlurRadius));
setDownsampleFactor(a.getInt(R.styleable.PxBlurringView_downsampleFactor,
defaultDownsampleFactor));
setOverlayColor(a.getColor(R.styleable.PxBlurringView_overlayColor, defaultOverlayColor));
a.recycle();
}
public void setBlurredView(View blurredView) {
mBlurredView = blurredView;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBlurredView != null) {
if (prepare()) {
// If the background of the blurred view is a color drawable, we use it to clear
// the blurring canvas, which ensures that edges of the child views are blurred
// as well; otherwise we clear the blurring canvas with a transparent color.
if (mBlurredView.getBackground() != null && mBlurredView.getBackground() instanceof ColorDrawable){
mBitmapToBlur.eraseColor(((ColorDrawable) mBlurredView.getBackground()).getColor());
}else {
mBitmapToBlur.eraseColor(Color.TRANSPARENT);
}
mBlurredView.draw(mBlurringCanvas);
blur();
canvas.save();
canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY());
canvas.scale(mDownsampleFactor, mDownsampleFactor);
canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
canvas.restore();
}
canvas.drawColor(mOverlayColor);
}
}
public void setBlurRadius(int radius) {
mBlurScript.setRadius(radius);
}
public void setDownsampleFactor(int factor) {
if (factor <= 0) {
throw new IllegalArgumentException("Downsample factor must be greater than 0.");
}
if (mDownsampleFactor != factor) {
mDownsampleFactor = factor;
mDownsampleFactorChanged = true;
}
}
public void setOverlayColor(int color) {
mOverlayColor = color;
}
private void initializeRenderScript(Context context) {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
}
protected boolean prepare() {
final int width = mBlurredView.getWidth();
final int height = mBlurredView.getHeight();
if (mBlurringCanvas == null || mDownsampleFactorChanged
|| mBlurredViewWidth != width || mBlurredViewHeight != height) {
mDownsampleFactorChanged = false;
mBlurredViewWidth = width;
mBlurredViewHeight = height;
int scaledWidth = width / mDownsampleFactor;
int scaledHeight = height / mDownsampleFactor;
// The following manipulation is to avoid some RenderScript artifacts at the edge.
scaledWidth = scaledWidth - scaledWidth % 4 + 4;
scaledHeight = scaledHeight - scaledHeight % 4 + 4;
if (mBlurredBitmap == null
|| mBlurredBitmap.getWidth() != scaledWidth
|| mBlurredBitmap.getHeight() != scaledHeight) {
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight,
Bitmap.Config.ARGB_8888);
if (mBitmapToBlur == null) {
return false;
}
mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight,
Bitmap.Config.ARGB_8888);
if (mBlurredBitmap == null) {
return false;
}
}
mBlurringCanvas = new Canvas(mBitmapToBlur);
mBlurringCanvas.scale(1f / mDownsampleFactor, 1f / mDownsampleFactor);
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
}
return true;
}
protected void blur() {
mBlurInput.copyFrom(mBitmapToBlur);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(mBlurredBitmap);
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mRenderScript != null){
mRenderScript.destroy();
}
}
private int mDownsampleFactor;
private int mOverlayColor;
private View mBlurredView;
private int mBlurredViewWidth, mBlurredViewHeight;
private boolean mDownsampleFactorChanged;
private Bitmap mBitmapToBlur, mBlurredBitmap;
private Canvas mBlurringCanvas;
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
}
Is there any way to modify the BlurringView.java class so the BlurringView xml object can be used like a RelativeView or something similar? I want to be able to have the BlurringView be the parent to other objects.
I need this, because I'm using the BlurringView together with a SlidingUpPanelLayout (https://github.com/umano/AndroidSlidingUpPanel) and the SlidingUpPanelLayout only allows two children for it to function. Thus I can't just overlay items over the BlurringView. I need the BlurringView to be their parent.
Please let me know if you need clarification.
-R
Blockquote Did you try to do an extract method of your constructor, implement the same methon in each of the three default constructors and extend from RelativeLayout?
The RelativeLayout actually have a onDraw method, maybe isn't being called because you didn't invoke the method setWillNotDraw(false), try using that method to trigger the onDraw or use an invalidate() at the end of the constructors.
#astinx answered this question.
All I had to do was change the BlurringView class to extend a RelativeLayout instead of a View. I then called the setWillNotDraw(false) method as well as the invalidate() method in the constructor and now I'm able to populate the BlurringView with children! Magic.
-R
I am working a project on ParallaxViewPager. I have 3 pages. Everything working normal and the background is parallax as supposed to. But when I add parallaxViewPager.setCurrentItem(2); the background does not appear and it gets default android background. Here is the full code:
MainActivity:
package com.example.myapplication3.app;
import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import android.widget.RelativeLayout;
import com.andraskindler.parallaxviewpager.ParallaxViewPager;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ParallaxViewPager parallaxViewPager = ((ParallaxViewPager) findViewById(R.id.parallaxviewpager));
parallaxViewPager.setOverlapPercentage(0.25f);
parallaxViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
parallaxViewPager.setCurrentItem(2);
}
}
PagerAdapter:
package com.example.myapplication3.app;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
this.fragments = new ArrayList<Fragment>();
fragments.add(new view1());
fragments.add(new view2());
fragments.add(new view3());
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
}
View 1,2,3:
package com.example.myapplication3.app;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class view1 extends Fragment { //view2, view3 on the others
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.view1, container, false); //same here view 2,3
return rootView;
}
}
and the layouts are:
MainActivitys' layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:ignore="MergeRootFrame" >
<com.andraskindler.parallaxviewpager.ParallaxViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parallaxviewpager"
android:layout_width="match_parent"
android:background="#drawable/sanfran"
android:layout_height="match_parent"/>
</RelativeLayout>
View 1,2,3' layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
Thanks in advance for your time and help.
I had the same problem with this component. It something like a conflict between the canvas and the destination rect so anything is drawn where it should be done.
I made a fork of the original source repository to update it. When setCurrentItem is invoked I check the position and store an adjust value that I use later in the onDraw method.
You can check it at https://github.com/yuraksisa/parallaxviewpager
Here is a modification of the ParallaxViewPager:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
#SuppressLint("NewApi")
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;
public Bitmap bitmap;
private Rect source, destination;
private int scaleType;
private Paint paint;
private OnPageChangeListener secondOnPageChangeListener;
public ParallaxViewPager(Context context) {
super(context);
init();
}
public ParallaxViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
source = new Rect();
destination = new Rect();
scaleType = FIT_HEIGHT;
setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (bitmap != null) {
if (position == 0) {
source.left = (int) ((getWidth() / 8) * positionOffset);
source.right = getWidth() + source.left;
destination.left = (int) ((getWidth() * positionOffset) - getWidth());
destination.right = (int) (0 + (getWidth() * positionOffset));
} else {
source.left = (int) ((getWidth() / 8) - (getWidth() / 8)
* positionOffset);
source.right = getWidth() + source.left;
destination.left = (int) ((0 - getWidth()
* positionOffset));
destination.right = (int) (getWidth() - (getWidth() * positionOffset));
}
postInvalidate();
}
if (secondOnPageChangeListener != null) {
secondOnPageChangeListener.onPageScrolled(position,
positionOffset, positionOffsetPixels);
}
}
#Override
public void onPageSelected(int position) {
if (secondOnPageChangeListener != null) {
secondOnPageChangeListener.onPageSelected(position);
}
}
#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.");
}
source.top = 0;
source.bottom = bitmap.getHeight();
}
/**
* 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;
}
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");
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, paint);
}
}
public void addOnPageChangeListener(OnPageChangeListener listener) {
secondOnPageChangeListener = listener;
}
}
And here is how to use:
viewPager = (ParallaxViewPager)findViewById(R.id.lock_viewpager);
viewPager.setBackgroundDrawable(new BitmapDrawable());
viewPager.setAdapter(new MyViewPagerAdapter());
viewPager.addOnPageChangeListener(this);
viewPager.setCurrentItem(mList.size());