I have a custom ImageView class (I don't think the code in the class matters but I'll include it here anyway incase it is needed):
package com.example.vroy.customcirclebuttontest;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
public class CircleButton extends ImageView {`
private int radius;
private int x;
private int y;
public CircleButton(Context context) {
super(context);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs) {
super(context, attrs);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
constructorTask();
}
private void constructorTask() {
x = 300;
y = 300;
radius = 100;
}
#Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
Log.i("Button Logger","Button Pressed");
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, radius, GameView.green);
Log.i("Drawing status", "CircleButton Drawing..." + " " + Integer.toString(this.x) + "," + Integer.toString(this.y));
}
}
I declare a instance of this class in my activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testCircleButton = new CircleButton(getApplicationContext());
makeFullScreen();
testCircleButton.getLayoutParams().width = 200;
testCircleButton.getLayoutParams().height = 200;
RelativeLayout screenLayout = (RelativeLayout) findViewById(R.id.screenLayout);
screenLayout.addView(testCircleButton);
Log.i("Button Status","Adding Button To Layout");
}
However when I do this I get a "null pointer" exception.
How can I fix this?
It's because your testCircleButton is not attached to the Activity. It has no LayoutParams if it's not added to any parent.
Use
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(200, 200) // I'm not sure about constructor arguments.
testCircleButton.setLayoutParams(lp);
Use different LayoutParams if your root layout is different(FrameLayout etc).
Related
I am trying to create a todo list app. I am adding entered text to the listitems on the enter, it's working fine without the style.
After adding style with paint and drawing its giving null pointer exception. I am initializing all the drawing and paint objects in the init method of "ToDoListItemView". I tried with "log.i" but it's not entering into ToDoListItemView's init method.
Here is my code.
MainActivity:
package todo.prac.com.todolist;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView mylistView=(ListView)findViewById(R.id.myListView);
final EditText editText=(EditText)findViewById(R.id.myEditText);
final ArrayList<String>todoItems=new ArrayList<String>();
final ArrayAdapter<String>adapter;
adapter=new ArrayAdapter<String>(this,R.layout.todolist_view,todoItems);
mylistView.setAdapter(adapter);
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(keyEvent.getAction()==KeyEvent.ACTION_DOWN){
if(i==KeyEvent.KEYCODE_ENTER){
todoItems.add(0,editText.getText().toString());
adapter.notifyDataSetChanged();
editText.setText("");
return true;
}
}
return false;
}
});
}
}
and my ToDoListItemView
package todo.prac.com.todolist;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
public class ToDoListItemView extends TextView {
private Paint marginPaint;
private Paint linePaint;
private int paperColor;
private float margin;
public ToDoListItemView(Context context) {
super(context);
}
public ToDoListItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ToDoListItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ToDoListItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private void init(){
Log.i("init","inside");
Resources myResources=getResources();
marginPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
marginPaint.setColor(myResources.getColor(R.color.notepad_margin));
linePaint.setColor(myResources.getColor(R.color.notepad_line));
paperColor=myResources.getColor(R.color.notepad_paper);
margin=myResources.getDimension(R.dimen.notepad_margin);
}
#Override
public void onDraw(Canvas canvas){
canvas.drawColor(paperColor);
if(linePaint==null){
Log.i("linepaint","is null");
}
canvas.drawLine(0,0,getMeasuredHeight(),0,linePaint);
canvas.drawLine(0,getMeasuredHeight(),getMeasuredWidth(),getMeasuredHeight(),linePaint);
canvas.drawLine(margin,0,margin,getMeasuredHeight(),marginPaint);
canvas.save();
canvas.translate(margin,0);
super.onDraw(canvas);
canvas.restore();
}
}
and my TodoList layout xml:
<?xml version="1.0" encoding="utf-8"?>
<todo.prac.com.todolist.ToDoListItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:padding="10dp"
android:scrollbars="vertical"
android:textColor="#color/notepad_text"
android:fadingEdge="vertical"
/>
You are not calling the init() method inside the constructors of your class.
public ToDoListItemView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ToDoListItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ToDoListItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
I solve this problem by this method.
public ToDoListItemView(Context context) {
this(context,null);
}
public ToDoListItemView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public ToDoListItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// initiate your some things
}
Pay attention to: only one super. others in place of this .
I have a custom CircleButton class:
public class CircleButton extends ImageView {
private int radius;
private int x;
private int y;
public CircleButton(Context context) {
super(context);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs) {
super(context, attrs);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
constructorTask();
}
public CircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
constructorTask();
}
private void constructorTask() {
x = 300;
y = 300;
radius = 100;
}
#Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
Log.i("Button Logger","Button Pressed");
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, radius, GameView.green);
Log.i("Drawing status", "CircleButton Drawing...");
}
}
I have a single activity. This activity contains a relative layout with a single custom view.
Here is the custom view:
public class GameView extends View {
public static Paint green = new Paint();
public GameView(Context context) {
super(context);
green.setARGB(255,0,255,0);
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
green.setARGB(255, 0, 255, 0);
}
public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
green.setARGB(255, 0, 255, 0);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i("GameView Draw Status","Drawing...");
Main.testButton.invalidate();
invalidate();
}
}
And here is the activity code:
public class Main extends AppCompatActivity {
public static CircleButton testButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testButton = new CircleButton(getApplicationContext());
makeFullScreen();
RelativeLayout screenLayout = (RelativeLayout) findViewById(R.id.screenLayout);
screenLayout.addView(testButton);
}
private void makeFullScreen() {...}
}
For some reason my testButton is not being drawn. Why is it not being drawn?
EDIT ONE: Here is the XML I have.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
tools:context="com.example.vroy.customcirclebuttontest.Main"
android:id="#+id/screenLayout">
<com.example.vroy.customcirclebuttontest.GameView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black"
android:id="#+id/gameScreen" />
</RelativeLayout>
EDIT TWO: I did some further debugging by adding a normal button to the relative layout and it worked fine.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testCircleButton = new CircleButton(getApplicationContext());
makeFullScreen();
testButton = new Button(getApplicationContext());
testButton.setX(100);
testButton.setY(100);
testButton.setText("HELLO WORLD");
RelativeLayout screenLayout = (RelativeLayout) findViewById(R.id.screenLayout);
screenLayout.addView(testCircleButton);
screenLayout.addView(testButton);
Log.i("Button Status","Adding Button To Layout");
}
For some reason by circleButton is not working but a normal button is.
You are not specifying the size of the View (layout_width and layout_height), and thus your view is getting rendered inside a 0px by 0px space and thus invisible.
You can set those programatically using LayoutParams before adding your views to the layout.
For example with absolute size:
testButton.setLayoutParams(new ViewGroup.LayoutParams(100,100));
Although keep in mind the difference between px and dip. You would probably want to set the values using your internal radius attribute instead of harcoding them.
I'm trying to make a reusable component that slides its content.
Here is the code of the component :
package com.example.components;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SlideRightNLeft extends LinearLayout{
private LinearLayout.LayoutParams params1;
private LinearLayout.LayoutParams params2;
public SlideRightNLeft(Context context) {
super(context);
}
public SlideRightNLeft(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideRightNLeft(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void slideRight(){
int x = 0;
while(x < 300) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
params1 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params1.setMargins(300 - x, 1, 1, 1);
params2 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params2.setMargins(x, 1, 1, 1);
this.getChildAt(0).setLayoutParams(params1);
this.getChildAt(1).setLayoutParams(params2);
x++;
this.invalidate();
}
}
}
Unfortunately it won't slide. In can suppose I have to use runOnUiThread to actually see it slide, but it needs an activity, and as I am in the component, I don't have access to it.
You can use the Context thats passed into the constructor to access the runOnUiThread method.
((Activity)context).runOnUiThread(new Runnable(){
});
I am trying to make a custom view that is square, using the width as the height. I am also using a pre-defined layout which I inflate as it's UI. As soon as I overrode onMeasure, the custom view no longer appears. Here is my code:
public class MyView extends RelativeLayout{
public MyView(Context context) {
super(context);
addView(setupLayout(context));
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
addView(setupLayout(context));
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
addView(setupLayout(context));
}
private View setupLayout(Context context) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View myView = inflater.inflate(R.layout.view_layout, null);
return myView;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(widthMeasureSpec));
}
}
I have 2 questions:
How do I override onMeasure so that it draws my view the way I am expecting it to?
Is there any way I can make this more efficient in terms of the view hierarchy (i.e. not be putting a RelativeLayout inside a RelativeLayout)
You can use this code from Jan Němec's answer to a similar question :
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SquareLayout extends LinearLayout {
public SquareLayout(Context context) {
super(context);
}
public SquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (width > (int)(mScale * height + 0.5)) {
width = (int)(mScale * height + 0.5);
} else {
height = (int)(width / mScale + 0.5);
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
}
}
Or try to use this library project.
I want to do a custom horizontal navigation bar, which consists of as many dots as i have pages.
My thoughts are:
Create a Custom ListView
Create Class Dot extends from View
Add all these dots to the custom listview dynamically..
Is this right to do so?
EDIT:
public class NavigationBarLesson extends LinearLayout {
private LessonConfig config = LessonConfig.getInstance();
private ArrayList<NavigationCircle> navigationCircles;
private int pageCount;
public NavigationBarLesson(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NavigationBarLesson(Context context) {
super(context);
init(context);
}
private void init(Context context) {
Log.i("init","yes");
pageCount = config.getLektionCount();
navigationCircles = new ArrayList<NavigationCircle>();
for(int i=0; i < pageCount; i++){
this.addView(new NavigationCircle(context));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = View.MeasureSpec.getSize(widthMeasureSpec);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
}
public class NavigationCircle extends ImageView{
private static Bitmap img;
private Bitmap activeImg;
public NavigationCircle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public NavigationCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public NavigationCircle(Context context) {
super(context);
init();
}
public void init() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 5, 0);
this.setLayoutParams(lp);
this.setBackgroundResource(R.drawable.upcoming_pages);
}
public Bitmap getImg() {
return img;
}
public Bitmap getActiveImg() {
return this.activeImg;
}
}
Well, I do not see why you would need a ListView. Create a horizontal LinearLayout with some simple views like TextView's or ImageView's etc added dynamically to the LinearLayout.