I'm trying to display a grid in my Android application. I'm using the "onDraw" method of a custom view I created for this purpose.
The problem is that the result is very strange, not all lines are drawn and some artifacts are visible. May I have your help to solve this?
Here's the code I use:
public class GridView extends View {
private int cellHeight;
private int cellWidth;
private int cellRows = 16;
private int cellColumns = 16;
private Paint lines = new Paint();
public GridView(Context context) {
super(context);
init();
}
public GridView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
lines.setStyle(Paint.Style.FILL_AND_STROKE);
lines.setColor(Color.BLACK);
cellWidth = getWidth() / cellColumns;
cellHeight = getHeight() / cellRows;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
cellWidth = getWidth() / cellColumns;
cellHeight = getHeight() / cellRows;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
for (int i = 0; i < cellRows; i++)
{
canvas.drawLine(0, i * cellHeight, getWidth(), i * cellHeight,
lines);
}
for (int i = 0; i < cellColumns; i++)
{
canvas.drawLine(i * cellWidth, 0, i * cellWidth, getHeight(),
lines);
}
}
}
This is what I get in EMULATOR:
http://hpics.li/ca14cd1
Moreover, in Android preview (when designing activity), I can see the result expected:
http://hpics.li/7b97afb
The problem occurs when the application is really started.
Related
I want to makes one layer fade into the beneath layers (or transparency depends on how you see it) according to a gradient. A so-called transparent (alpha) gradient mask. I am looking for a solution similar to this but on android instead of ios:
I have tried this solution but as mentioned in the comments, the overlay is not making the layer beneath transparent, it only makes it fade to a specified color.
Any suggestions?
You could use Android-FadingEdgeLayout library.
Or here's a subclassed framelayout based on the said lib:
public class AlphaGradientLayout extends FrameLayout {
private static final int DEFAULT_GRADIENT_SIZE_DP = 80;
public static final int FADE_EDGE_TOP = 1;
private static final int DIRTY_FLAG_TOP = 1;
private static final int[] FADE_COLORS = new int[]{Color.TRANSPARENT, Color.BLACK};
private boolean fadeTop;
private int gradientSizeTop;
private Paint gradientPaintTop;
private Rect gradientRectTop;
private int gradientDirtyFlags;
public AlphaGradientLayout(Context context) {
super(context);
init(null, 0);
}
public AlphaGradientLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public AlphaGradientLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, 0);
}
private void init(AttributeSet attrs, int defStyleAttr) {
int defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_GRADIENT_SIZE_DP,
getResources().getDisplayMetrics());
if (attrs != null) {
TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.AlphaGradientLayout, defStyleAttr, 0);
int flags = arr.getInt(R.styleable.FadingEdgeLayout_fel_edge, 0);
fadeTop = (flags & FADE_EDGE_TOP) == FADE_EDGE_TOP;
gradientSizeTop = arr.getDimensionPixelSize(R.styleable.FadingEdgeLayout_fel_size_top, defaultSize);
if (fadeTop && gradientSizeTop > 0) {
gradientDirtyFlags |= DIRTY_FLAG_TOP;
}
arr.recycle();
} else {
gradientSizeTop = defaultSize;
}
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
gradientPaintTop = new Paint(Paint.ANTI_ALIAS_FLAG);
gradientPaintTop.setXfermode(mode);
gradientRectTop = new Rect();
}
#Override
public void setPadding(int left, int top, int right, int bottom) {
if (getPaddingTop() != top) {
gradientDirtyFlags |= DIRTY_FLAG_TOP;
}
super.setPadding(left, top, right, bottom);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (h != oldh) {
gradientDirtyFlags |= DIRTY_FLAG_TOP;
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
int newWidth = getWidth(), newHeight = getHeight();
boolean fadeAnyEdge = fadeTop;
if (getVisibility() == GONE || newWidth == 0 || newHeight == 0 || !fadeAnyEdge) {
super.dispatchDraw(canvas);
return;
}
if ((gradientDirtyFlags & DIRTY_FLAG_TOP) == DIRTY_FLAG_TOP) {
gradientDirtyFlags &= ~DIRTY_FLAG_TOP;
int actualHeight = getHeight() - getPaddingTop() - getPaddingBottom();
int size = Math.min(gradientSizeTop, actualHeight);
int l = getPaddingLeft();
int t = getPaddingTop();
int r = getWidth() - getPaddingRight();
int b = t + size;
gradientRectTop.set(l, t, r, b);
LinearGradient gradient = new LinearGradient(l, t, l, b, FADE_COLORS, null, Shader.TileMode.CLAMP);
gradientPaintTop.setShader(gradient);
}
int count = canvas.saveLayer(0.0f, 0.0f, (float) getWidth(), (float) getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
if (fadeTop && gradientSizeTop > 0) {
canvas.drawRect(gradientRectTop, gradientPaintTop);
}
canvas.restoreToCount(count);
}
}
Then move the items that you want faded inside this layout
And here's what you should get
I draw a rectangle in the activity_main folder and I get these values to be drawn in integer.xml, but I do not want to do that.getInteger (R.integer.scanner_rect_width and getResources (). getInteger (R.integer.scanner_rect_height I get these values from the integer.xml folder, but how do I assign these values by creating a mainActivity object?
ScannerOverlay scannerOverlay = new ScannerOverlay (); as
ScanerOverlay.java
public class ScannerOverlay extends ViewGroup {
private float left, top, endY;
private int rectWidth, rectHeight;
private int frames;
private boolean revAnimation;
private int lineColor, lineWidth;
public ScannerOverlay(Context context) {
super(context);
}
public void setWidth(int pixels) {
rectWidth = pixels;
requestLayout();
invalidate();
}
public void setHeight(int pixels) {
rectHeight = pixels;
requestLayout();
invalidate();
}
public ScannerOverlay(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScannerOverlay(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ScannerOverlay,
0, 0);
rectWidth = a.getInteger(R.styleable.ScannerOverlay_square_width, 150);
rectHeight = a.getInteger(R.styleable.ScannerOverlay_square_height, 150);
lineColor = a.getColor(R.styleable.ScannerOverlay_line_color, ContextCompat.getColor(context, R.color.scanner_line));
lineWidth = a.getInteger(R.styleable.ScannerOverlay_line_width, getResources().getInteger(R.integer.line_width));
frames = a.getInteger(R.styleable.ScannerOverlay_line_speed, getResources().getInteger(R.integer.line_width));
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
left = (w - dpToPx(rectWidth)) / 2;
top = (h - dpToPx(rectHeight)) / 2;
endY = top;
super.onSizeChanged(w, h, oldw, oldh);
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw transparent rect
int cornerRadius = 0;
Paint eraser = new Paint();
eraser.setAntiAlias(true);
eraser.setColor(Color.WHITE);
RectF rect = new RectF(left, top, dpToPx(rectWidth) + left, dpToPx(rectHeight) + top);
canvas.drawRoundRect(rect, (float) cornerRadius, (float) cornerRadius, eraser);
// draw horizontal line
Paint line = new Paint();
line.setColor(lineColor);
line.setStrokeWidth(Float.valueOf(lineWidth));
// draw the line to product animation
if (endY >= top + dpToPx(rectHeight) + frames) {
revAnimation = true;
} else if (endY == top + frames) {
revAnimation = false;
}
// check if the line has reached to bottom
if (revAnimation) {
endY -= frames;
} else {
endY += frames;
}
canvas.drawLine(left, endY, left + dpToPx(rectWidth), endY, line);
invalidate();
}
}
activity_main.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.yavuz.myapplication.MainActivity">
<com.example.yavuz.myapplication.ScannerOverlay
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#44000000"
app:line_color="#7323DC"
app:line_speed="6"
app:line_width="4"
/>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
You have to create setters methods for width and height as below:
public void setWidth(int pixels) {
rectWidth = pixels;
requestLayout();
invalidate();
}
public void setHeight(int pixels) {
rectHeight = pixels;
requestLayout();
invalidate();
}
Then to use it MainActivity you use it as below:
ScannerOverlay scannerOverlay = new ScannerOverlay();
scannerOverlay.setWidth(200); // for example
scannerOverlay.setHeight(300);
setContentView(scannerOverlay);
I suppose that ScannerOverlay is a custom view.
from xml layout:
<thepackage_where_theclass.ScannerOverlay
app:scanner_rect_width="200"
app:scanner_rect_height="200"/>
How can we create a custom gradient background view pager like timely app.
I am able to create radial gradient like below
in my tab fragment xml
<view
android:id="#+id/myView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerHorizontal="true"
class="com.android.MyView" />
MyView.java
public class MyView extends View {
Paint BackPaint = new Paint();
Context MyContext;
public MyView(Context context) {
super(context);
init(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context ctx) {
MyContext = ctx;
BackPaint.setStyle(Paint.Style.FILL);
BackPaint.setColor(Color.BLACK);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
#Override
protected void onDraw(Canvas canvas) {
float w, h, cx, cy, radius;
w = getWidth();
h = getHeight();
cx = w / 2;
cy = h / 2;
if (w > h) {
radius = h / 4;
} else {
radius = w / 4;
}
canvas.drawRect(0, 0, w, h, BackPaint);
Paint MyPaint = new Paint();
MyPaint.setStyle(Paint.Style.FILL);
SharedPreferences prefs;
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
float shaderCx = 0;
float shaderCy = 0;
float shaderRadius = w;
int shaderColor0 = Color.WHITE;
int shaderColor1 = Color.BLACK;
shaderColor0 = prefs.getInt("pref1", 0);
shaderColor1 = prefs.getInt("pref2", 0);
MyPaint.setAntiAlias(true);
Shader radialGradientShader;
radialGradientShader = new RadialGradient(shaderCx, shaderCy,
shaderRadius, shaderColor0, shaderColor1,
Shader.TileMode.MIRROR);
MyPaint.setShader(radialGradientShader);
canvas.drawRect(0, 0, w, h, MyPaint);
// canvas.drawRect(0, 0, w, h, MyPaint);
// canvas.drawCircle(cx, cy, radius, MyPaint);
};
}
but how can we create separate gradient for each tab?
I am trying to draw a circle which will represent time and after each complete circle, I want to change the color to indicate to the user that the next time unit has started and draw over the previous circle color rather than reset as shown in the example below.
I am trying to draw a circle using Draw Arc method in the following way
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
The sweep angle is controlled by an Object Animator :
outerCircleAnimator = ObjectAnimator.ofFloat(timeView, TimeView.SET_SWEEPWANGLE, 0, 360);
With the following code, I am able to achieve the following
The following is my View class :
public class TimeView extends View {
final protected Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final protected Paint fgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final protected Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF mRect = new RectF();
private float sweepAngle;
private float radiusInDPI = 100;
private float radiusInPixels;
private float strokeWidthInDPI = 4;
private float stokeWidthInPixels;
private float dpi;
private int heightByTwo;
private int widthByTwo;
public TimeView(Context context) {
super(context);
init();
}
public TimeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
heightByTwo = h / 2;
widthByTwo = w / 2;
mRect = new RectF(w / 2 - radiusInPixels, h / 2 - radiusInPixels, w / 2 + radiusInPixels, h / 2 + radiusInPixels);
}
private void init() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
dpi = metrics.density;
radiusInPixels = dpi * radiusInDPI;
stokeWidthInPixels = dpi * strokeWidthInDPI;
bgPaint.setStrokeWidth(stokeWidthInPixels);
bgPaint.setStyle(Paint.Style.STROKE);
bgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
fgPaint.setStrokeWidth(stokeWidthInPixels);
fgPaint.setStyle(Paint.Style.STROKE);
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
textPaint.setTextSize(24 * 3);
textPaint.setStyle(Paint.Style.STROKE);
textPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawCircle(widthByTwo, heightByTwo, radiusInPixels, bgPaint);
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
}
public static final Property<TimeView, Float> SET_SWEEPWANGLE =
new Property<TimeView, Float>(Float.class, "outerCircleRadiusProgress") {
#Override
public Float get(TimeView object) {
return object.getSweepAngle();
}
#Override
public void set(TimeView object, Float value) {
object.setSweepAngle(value);
}
};
public float getSweepAngle() {
return sweepAngle;
}
public void setSweepAngle(float sweepAngle) {
Log.v("Testing", "Sweep angle is " + sweepAngle + " " + (sweepAngle + 270));
this.sweepAngle = sweepAngle;
postInvalidate();
}
public void setColor(boolean change) {
if (change) {
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.mint_green));
} else {
fgPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
}
}
}
onDraw is used to draw on the empty canvas. It is always starting from the beginning. You'll need to save the last fgPaint and:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (lastFgPaint != null) {
canvas.drawArc(mRect, sweepAngle, 360, false, lastFgPaint);
}
canvas.drawArc(mRect, 270, sweepAngle, false, fgPaint);
}
Do you know how can I scale the selected item in a Gallery? I know that apparently getScale() and getAlpha() were removed from 0.9 SDK. So how could I accomplish the same effect?
Thanks
Maybe it's too late to answer, but I found this question when searching something else.
I did it by having a custom gallery and overriding getChildStaticTransformation() and adding some other things.
Here is an example
private int centerOfGallery;
public CustomGallery(Context context) {
super(context);
init();
}
public CustomGallery(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setStaticTransformationsEnabled(true);
}
private int getCenterWidthOfView(View child) {
return child.getLeft() + child.getWidth() / 2;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerOfGallery = (w - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
}
#Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
mCamera.save();
final Matrix matrix = t.getMatrix();
final int centerWidthOfChild = getCenterWidthOfView(child);
final int delta = centerOfGallery - centerWidthOfChild;
final float scale = (float)(maxScale - Math.abs(delta) * 0.5f / centerOfGallery);
mCamera.getMatrix(matrix);
matrix.preScale(scale, scale);
matrix.preTranslate(-1, -1);
matrix.postTranslate(1, 1);
mCamera.restore();
if (version >= 15) { // For Jelly Bean hack
child.invalidate();
}
return true;
}
where
maxScale is the maximum scale you want for selected item (e.g. 1.5f)
After that, be careful about the spacing between items in the gallery when scaling them. You can use setSpacing() if necessary.
Hope this helps
Seb
try this
Image in Canvas with touch events