Having a problem with Android Custom components. Trying to draw an oval shape but nothing happening.
I have this line in layout xml file
<android.project.realtimedata.DemoView android:id="#+id/demoView"
android:layout_width="fill_parent" android:layout_height="wrap_content" />
Here is the code for my custom component class.
package android.project.realtimedata;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.view.View;
public class DemoView extends View{
ShapeDrawable thisGauge = null;
public DemoView(Context context){
super(context);
init();
}
public DemoView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init(){
int x = 10;
int y = 10;
int width = 300;
int height = 50;
thisGauge = new ShapeDrawable(new OvalShape());
thisGauge.getPaint().setColor(0xff74AC23);
thisGauge.setBounds(x, y, x + width, y + height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
thisGauge.draw(canvas);
}
}
I also have this line in onCreate method of Activity
demoView = (DemoView) findViewById(R.id.demoView);
Whenever I launch the application the custom component is not there.
I tried looking at it from LogCat and it definitely gets created.
What am I missing here?
Thanks in advance.
Make sure that you calling findViewById(R.id.demoView) after calling setContentView(...). To ensure that your view is being inflated, you can call Log.d("DemoView", "Created") from inside your DemoView constructor.
Related
I wanted to create a custom button or view progrmatically with a simple image and text as shown in image,
where edge is of button not of image.
Please don't use xml.
Any help would be greatly appreciated. I wanted to learn and create custom view with canvas but since i am new one to canvas, i am not able to create it.
copy and paste below code, Hope this will give you your desired output..
Here is what I've got using this code Screenshot
XML code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black_alpha_30"
android:padding="15dp">
<RelativeLayout
android:id="#+id/relative_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/relative_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
Java code:
private RelativeLayout mRelativeLayout, mRelativeParent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRelativeLayout = (RelativeLayout) findViewById(R.id.relative_main);
mRelativeParent = (RelativeLayout) findViewById(R.id.relative_parent);
Button btnMain = new Button(MainActivity.this);
btnMain.setBackgroundColor(getResources().getColor(R.color.teal_600));
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 80);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
layoutParams.setMargins(15,15,15,15);
btnMain.setLayoutParams(layoutParams);
mRelativeLayout.addView(btnMain);
Button btnImage = new Button(MainActivity.this);
btnImage.setBackgroundDrawable(getResources().getDrawable(R.drawable.teal_bg));
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(150, 150);
layoutParams1.addRule(mRelativeParent.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
btnImage.setLayoutParams(layoutParams1);
mRelativeParent.addView(btnImage);
}
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
public class ContainerBox extends View {
private Paint textPaint;
private String mainText="Vikram Singh";
private String backgroundColour = "#FF8514";
private String textColour = "#1896bb";
private Bitmap leftIcon;
private Paint paintBackGround;
private Rect recBackGround;
private Paint paintImage ;
private Rect recImage;
public ContainerBox(Context context) {
super(context);
initializePaints(context);
}
public ContainerBox(Context context, AttributeSet attrs) {
super(context, attrs);
initializePaints(context);
}
public ContainerBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializePaints(context);
}
private void initializePaints(Context context) {
leftIcon = BitmapFactory.decodeResource(getResources(), R.drawable.icon_done);
paintImage = new Paint();
paintImage.setColor(Color.parseColor(backgroundColour));
paintImage.setStrokeWidth(3);
paintImage.setAntiAlias(true);
paintImage.setStyle(Paint.Style.FILL);
paintBackGround = new Paint();
paintBackGround.setColor(Color.parseColor(backgroundColour));
paintBackGround.setStrokeWidth(3);
paintBackGround.setAntiAlias(true);
paintBackGround.setStyle(Paint.Style.FILL);
textPaint = new Paint();
textPaint.setColor(Color.parseColor(textColour));
textPaint.setAntiAlias(true);
textPaint.setTextSize(4);
textPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(),leftIcon.getHeight()+40);
}
#Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = leftIcon.getHeight()+40;
int differenceHeight=height-25;
int differenceWidth=width-leftIcon.getWidth()+15;
recBackGround=new Rect(0,25,differenceWidth,differenceHeight);
canvas.drawRect(recBackGround,paintBackGround);
textPaint.setTextSize(15f);
float textWidth = textPaint.measureText(mainText);
int x = (int) ((recBackGround.width() - textWidth) / 2);
int y = (int) ((recBackGround.centerY() - (textPaint.descent() + textPaint.ascent())/2));
// draw text
canvas.drawText(mainText, x, y, textPaint);
recImage=new Rect(recBackGround.right,0,width,height);
canvas.drawRect(recImage,paintImage);
int left=recImage.width()/2-leftIcon.getWidth()/2;
int top=recImage.height()/2-leftIcon.getHeight()/2;
canvas.drawBitmap(leftIcon,recImage.left,top,paintImage);
super.onDraw(canvas);
}
After extending the ScrollView class I was able to easily be notified of the scrolling in realtime.
Now I need to capture the content of this scrollview in a very specific part.
Let's say I want to capture the top of the screen (matching parent width and a defined height, like 100dp). But only the content of the ScrollView and not the rest, if there is anything else on the top but not as part of the ScrollView.
I tried using on the scrollview :
setDrawingCacheEnabled(true);
getDrawingCache(true);
setDrawingCacheEnabled(false);
Then I tried to crop so that I get the part I want :
Bitmap.createBitmap(complete, 0, 0, width, height);
Results are very far from what I want to achieve and performance are very very poor and at some point I would get either a SIGENV or getDrawingCache(true) tries to use a recycled bitmap...
So how can I easily capture the content in the desired area without too much performance hit ?
Note: this process must be done as I am scrolling the content, so inside ScrollView's onScrollChanged(final int x, final int y).
Thanks !
Since the problem was fun I implemented it, it seems to work fine. I guess that you are recreating a Bitmap each time that's why goes slow.
The idea is like this, you create an area in the ScrollView that you want to copy (see Rect cropRect and Bitmap screenshotBitmap), it's full width and you just need to set the height. The view automatically set a scroll listener on itself and on every scroll it will copy that area. Note that setDrawingCacheEanbled(true) is called just once when the view is instantiated, it basically tells the view that you will call getDrawingCache(), which will return the Bitmap on which the view is drawing itself. It then copy the area of interest on screenshotBitmap and that's the Bitmap that you might want to use.
ScreenshottableScrollView.java
package com.example.lelloman.screenshottablescrollview;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.ScrollView;
/**
* Created by lelloman on 16-2-16.
*/
public class ScreenshottableScrollView extends ScrollView implements ViewTreeObserver.OnScrollChangedListener {
public interface OnNewScreenshotListener {
void onNewScreenshot(Bitmap bitmap);
}
private Bitmap screenshotBitmap = null;
private Canvas screenshotCanvas = null;
private int screenshotHeightPx = 0;
private OnNewScreenshotListener listener = null;
private Rect cropRect;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public ScreenshottableScrollView(Context context) {
super(context);
init();
}
public ScreenshottableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScreenshottableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ScreenshottableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init(){
setDrawingCacheEnabled(true);
getViewTreeObserver().addOnScrollChangedListener(this);
}
public void setOnNewScreenshotListener(OnNewScreenshotListener listener){
this.listener = listener;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(screenshotHeightPx != 0)
makeScrenshotBitmap(w,h);
}
public void setScreenshotHeightPx(int q){
screenshotHeightPx = q;
makeScrenshotBitmap(getWidth(), getHeight());
}
private void makeScrenshotBitmap(int width, int height){
if(screenshotBitmap != null) screenshotBitmap.recycle();
if(width == 0 || height == 0) return;
screenshotBitmap = Bitmap.createBitmap(width, screenshotHeightPx, Bitmap.Config.ARGB_8888);
screenshotCanvas = new Canvas(screenshotBitmap);
cropRect = new Rect(0,0,width,screenshotHeightPx);
}
#Override
public void onScrollChanged() {
if(listener == null) return;
Bitmap bitmap = getDrawingCache();
screenshotCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
screenshotCanvas.drawBitmap(bitmap,cropRect, cropRect,paint);
listener.onNewScreenshot(screenshotBitmap);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.lelloman.screenshottablescrollview.MainActivity">
<com.example.lelloman.screenshottablescrollview.ScreenshottableScrollView
android:id="#+id/scrollView"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.example.lelloman.screenshottablescrollview.ScreenshottableScrollView>
<View
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#ff000000"/>
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="100dp" />
</LinearLayout>
MainActivity.java
package com.example.lelloman.screenshottablescrollview;
import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StringBuilder builder = new StringBuilder();
Random random = new Random();
String AB = "abcdefghijklmnopqrstuvwxyz ";
for(int i=0;i<100;i++){
builder.append("\n\n"+Integer.toString(i)+"\n\n");
for(int j =0;j<1000;j++){
builder.append(AB.charAt(random.nextInt(AB.length())));
}
}
((TextView) findViewById(R.id.textView)).setText(builder.toString());
final ImageView imageView = (ImageView) findViewById(R.id.imageView);
ScreenshottableScrollView scrollView = (ScreenshottableScrollView) findViewById(R.id.scrollView);
scrollView.setScreenshotHeightPx((int) (getResources().getDisplayMetrics().density * 100));
scrollView.setOnNewScreenshotListener(new ScreenshottableScrollView.OnNewScreenshotListener() {
#Override
public void onNewScreenshot(Bitmap bitmap) {
Log.d("MainActivity","onNewScreenshot");
imageView.setImageBitmap(bitmap);
}
});
}
}
Is there a way to for a Bitmap object to have a transparent background instead of solid color.. I'd like my background image (set on XML layout to show)?
sadly, none of the other question worked for me, otherwise, I wouldn't be asking... THANKS IN ADVANCE! ALSO, any tips on how to get the animation to work with XML?
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.View;
public class Vehicle extends View{
Bitmap vehicle;
int x_axisMovement;
public Vehicle(Context context) {
super(context);
// TODO Auto-generated constructor stub
vehicle = BitmapFactory.decodeResource(getResources(), R.drawable.vehicle_bus);
x_axisMovement = 1024;
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT); // NOT WORKING
canvas.drawBitmap(vehicle, x_axisMovement, 400, null);
if(x_axisMovement > -256){
x_axisMovement -= 4;
}
else
{
x_axisMovement = 1024;
}
invalidate();
}
}
You can use it in xml and add it in a relative layout so youll have a view on top of another view
sample:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_launcher" >
<com.example.Vehicle
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Now com.example is the folder inside your src folder but you might have different folder just press ctrl+shift+o so it will automatically give you an hint to the class location.
Now your class
public class Vehicle extends View {
Bitmap vehicle;
int x_axisMovement;
public Vehicle(Context context) {
super(context);
init();
}
public Vehicle(Context context, AttributeSet s) {
super(context, s);
init();
}
public Vehicle(Context context, AttributeSet s, int style) {
super(context, s, style);
init();
}
public void init() {
vehicle = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
x_axisMovement = 1024;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT); // NOT WORKING
canvas.drawBitmap(vehicle, x_axisMovement, 400, null);
if (x_axisMovement > -256) {
x_axisMovement -= 4;
} else {
x_axisMovement = 1024;
}
invalidate();
}
}
You need to have that 3 constructor to enable the class to be used in an xml form.
now the result:
The background is the big ic_launcher and the image from your class in the small ic_launcher.
I am new to Android and I am still searching for good resources to mine.
My question involves the best way to draw a grid of rectangle shaped objects to the screen. I need each object displayed in the grid to have an initial text (or an int as text) value, and when a user touches that object, the text will change to a colored shape. Also, each object needs to be aware of (or be able to find out) the state of its immediate neighbors.
I don't know which class to extend in order to be able to have both text and shapes display on it, and be able to handle touch input.
Thank you for the help.
Edit:
I apologize, but I don't know how to be more clear. Perhaps some context will help. I have a main activity that takes an int value as input and creates an Intent which sends that value to another activity. That other activity then displays a grid of 100 random numbers. The user needs to select a series of grid spots and a certain number of those spots that the user chose will change from the random number to a colored shape. The spots that change are controlled by logic that I will provide in the code.
SO Q with basic android graph: Custom dynamic graph in Android
Android official guide on custom controls
https://developer.android.com/guide/topics/ui/custom-components.html
Android reference page on the View control, which you'll be expanding on
https://developer.android.com/reference/android/view/View.html
real world code example of a custom View control (you're particularly interested in onDraw() )
http://www.java2s.com/Open-Source/Android/App/ringdroid/com/ringdroid/WaveformView.java.htm
I know its a long time after the question has been asked but maybe it will help ppl :)
Add this to "attrs.xml" (or create new if needed)
<resources>
<declare-styleable name="RectanglesGridView">
<attr name="cellSize" format="dimension" />
<attr name="cellColor1" format="color" />
<attr name="cellColor2" format="color" />
</declare-styleable>
And this is the class - "RectanglesGridView.java"
package com.gilapps.movinglivewallpaper.UI.views;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import com.gilapps.movinglivewallpaper.R;
public class RectanglesGridView extends View {
private final static float DEFAULT_CELL_SIZE_DP = 10;
private final static int DEFAULT_CELL_COLOR1 = Color.GRAY;
private final static int DEFAULT_CELL_COLOR2 = Color.WHITE;
private int mColor1 = DEFAULT_CELL_COLOR1;
private int mColor2 = DEFAULT_CELL_COLOR2;
private float mCellSize;
private Paint mPaint;
private boolean mIsColor1;
private int mWidth;
private int mHeight;
public RectanglesGridView(Context context) {
super(context);
mCellSize = convertDpToPixel(DEFAULT_CELL_SIZE_DP);
mPaint = new Paint();
}
public RectanglesGridView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
loadAttributes(context, attrs);
}
public RectanglesGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
loadAttributes(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RectanglesGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mPaint = new Paint();
loadAttributes(context, attrs);
}
private void loadAttributes(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.RectanglesGridView,
0, 0);
try {
mCellSize = a.getDimension(R.styleable.RectanglesGridView_cellSize, convertDpToPixel(DEFAULT_CELL_SIZE_DP));
mColor1 = a.getColor(R.styleable.RectanglesGridView_cellColor1, DEFAULT_CELL_COLOR1);
mColor2 = a.getColor(R.styleable.RectanglesGridView_cellColor2, DEFAULT_CELL_COLOR2);
} catch (Exception e) {
mCellSize = convertDpToPixel(DEFAULT_CELL_SIZE_DP);
} finally {
a.recycle();
}
}
private float convertDpToPixel(float dp){
Resources resources = getContext().getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return px;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
for (float r=0;r<mHeight;r+=mCellSize) {
for (float c=0;c<mWidth;c+=mCellSize) {
mPaint.setColor(mIsColor1 ? mColor2 : mColor1);
mIsColor1 = !mIsColor1;
canvas.drawRect(c,r,c+mCellSize,r+mCellSize,mPaint);
}
mIsColor1 = !mIsColor1;
}
super.onDraw(canvas);
}
}
Usage:
<com.gilapps.movinglivewallpaper.UI.views.RectanglesGridView
app:cellColor1="#33000000"
app:cellColor2="white"
app:cellSize="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
add xmlns:app="http://schemas.android.com/apk/res-auto" to the root view
Change the package name if needed
Enjoy! :)
I am trying to implement an indicator in an application that shows the volume level of an external device. For this I've created a Layout which is supposed to have Rectangle children being drawn runtime depending on the current volume of said device.
How do I go about accomplishing this? Specifically I wish to draw these rectangles with heights matching that of their parent.
use a ProgressBar or SeekBar (with touch disabled) views and use their method setProgress(int value) to update.
I think this will help with your quest. It's quite easy actually, just make it a LinearLayout with some childs with equal width.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class CustomView extends LinearLayout {
private ArrayList<ImageView> views = new ArrayList<ImageView>();
public CustomView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context);
}
public CustomView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.setOrientation(LinearLayout.HORIZONTAL);
for (int i = 0; i < 10; i++) {
ImageView loadingPiece = new ImageView(context);
loadingPiece.setBackgroundColor(Color.RED);
this.addView(loadingPiece);
LayoutParams layoutParams = (LayoutParams)loadingPiece.getLayoutParams();
layoutParams.weight = 1.0f;
layoutParams.height = this.getHeight();
layoutParams.width = 0;
loadingPiece.setLayoutParams(layoutParams);
views.add(loadingPiece);
}
}
public void setPercentage(int amountToShow) {
for (int i = 0; i < views.size(); i++)
if (i < amountToShow)
views.get(i).setVisibility(View.VISIBLE);
else
views.get(i).setVisibility(View.INVISIBLE);
}
}
Hope it helps.