I've made a very simple customView, a gray rectangle with an arbitrary amount of red markings inside the rectangle marked by percentages.
public class DemoView extends View {
private ShapeDrawable mDrawable;
private ArrayList<ShapeDrawable> mMarks;
public DemoView(Context context, int[] marks) {
super(context);
int x = 0;
int y = 0;
int width = 100;
int height = 10;
// Timeline Initially empty
mDrawable = new ShapeDrawable(new RectShape());
mDrawable.getPaint().setColor(Color.GRAY);
mDrawable.setBounds(x, y, x + width, y + height);
// Add marks
if (marks != null && marks.length % 2 == 0) {
mMarks = new ArrayList<ShapeDrawable>(marks.length / 2);
ShapeDrawable mark;
for (int i = 1; i < marks.length; i = i + 2) {
mark = new ShapeDrawable(new RectShape());
mark.getPaint().setColor(Color.RED);
mark.setBounds(x + marks[i - 1], y, x + marks[i], y + height);
mMarks.add(mark);
}
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
if (mMarks != null)
for (ShapeDrawable mark : mMarks)
mark.draw(canvas);
}
}
However I can't figure out how to make use of the view. Each time I try to add more than one of the view in a linearlayout or relativelayout, I only see one of the views.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/llayout"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
/>
</LinearLayout>
Layout code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout ll = (LinearLayout) findViewById(R.id.llayout);
demoview = new DemoView(this, new int[]{10,15,35,60});
demoview.setId(ID_NUM++);
ll.addView(demoview);
demoview2 = new DemoView(this, new int[]{0,1,3,6});
demoview2.setId(ID_NUM++);
ll.addView(demoview2);
demoview3 = new DemoView(this, new int[]{25,60});
demoview3.setId(ID_NUM++);
ll.addView(demoview3);
demoview4 = new DemoView(this, new int[]{15,60});
demoview4.setId(ID_NUM++);
ll.addView(demoview4);
}
Results in:
Is this the wrong route to take? Am I missing some obvious key to using this view multiple times? If this is not the correct route is there some other method to making a custom shape? Perhaps extending rectShape?
Following Mibollma's advice, I watched the video above, a video from Google I/O 2009 about speeding up your UI.
The information is most definitely still applicable two years later. Not only was I able to speed up all of my ListViews through the use of ViewHolder, I was able to find the answer to my question.
When creating a custom view, two methods must be overriden, the first is listed above: onDraw.
The missing method? onMeasure(). More information can be found here.
Related
So I know there are a lot of questions like this, but none of the answers have had any effect.
I have a custom View that I am trying to put into a ScrollView (all programmatically). The ScrollView itself works fine, is where it needs to be, does what it's supposed to do and when tested by adding a TextView to it, still did what it was supposed to.
However when I create my custom View the view doesn't draw and it is as if nothing is there. The draw(Canvas canvas) method is never called (also tried with onDraw even though I don't know what the difference is but it still didn't work).
I have tried things such as setWillNotDraw(false) in the constructor, called View.invalidate() every time I want it to draw/redraw, nothing appears to be working.
Here is the relevant code:
ScrollView Initialized
sv = new ScrollView(env.getContext());
ScaledBounds sb = UsefulMethods.getScaledBounds(50, 200, BBEnvironment.WIDTH - 100, BBEnvironment.HEIGHT - 300);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(sb.getWidth(), sb.getHeight());
sv.setTranslationX(sb.getX());
sv.setTranslationY(sb.getY());
sv.setLayoutParams(params);
sv.setScrollbarFadingEnabled(false);
sv.setScrollBarSize(0);
sv.setBackgroundColor(Color.argb(150, 0, 255, 0));
Custom View Class
private class LevelSelectionView extends View {
private ArrayList<LevelButton> levelButtons;
public LevelSelectionView(Context context) {
super(context);
levelButtons = new ArrayList<LevelButton>();
int x = 20;
int y = 20;
for(int i = 0; i < 20; i++) {
levelButtons.add(new LevelButton(x, y, 100, 100, 10, i + 1));
levelButtons.get(i).unlock();
x += 100 + 20;
}
setWillNotDraw(false);
}
public void draw(Canvas canvas) {
super.draw(canvas);
System.out.println("is being called"); //except it isn't
Paint paint = new Paint();
paint.setAntiAlias(env.getCurrentSettings().getAntialias());
for(LevelButton lb : levelButtons)
lb.drawObject(canvas, paint);
}
}
Creating and adding custom view
LevelSelectionView lsView = new LevelSelectionView(sv.getContext());
lsView.setLayoutParams(new RelativeLayout.LayoutParams(sv.getLayoutParams().width, 1000));
lsView.setBackgroundColor(Color.argb(150, 0, 0, 255));
sv.addView(lsView);
Why is it not drawing and how do I get it to draw?
I tried like that and it works:
public class LevelSelectionView extends View {
private final static String TAG = LevelSelectionView.class.getSimpleName();
private final ArrayList<Button> mButtons;
private final Paint mPaint;
public LevelSelectionView(Context context) {
super(context);
mButtons = new ArrayList<Button>();
int x = 20;
int y = 20;
mPaint = new Paint();
// mPaint.setAntiAlias(env.getCurrentSettings().getAntialias());
Button myButton;
for (int i = 0; i < 20; i++) {
myButton = new Button(context);
myButton.setLayoutParams(new ViewGroup.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
mButtons.add(myButton);
// mButtons.get(i).unlock();
// x += 100 + 20;
}
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Button lb : mButtons)
Log.d(TAG, "is being called"); //except it isn't
// lb.drawObject(canvas, mPaint);
}
}
I don't know what is your LevelButton as it's a custom class.
I create the LevelSelectionView like that, using a FrameLayout but it's the same idea with the ScrollView:
FrameLayout container = (FrameLayout) findViewById(R.id.container);
LevelSelectionView view = new LevelSelectionView(this);
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
container.addView(view);
Maybe your issue comes from the LevelButton or the context you use (as you use the Context from env to create the ScrollView and the Context of the ScrollView to create your custom class). You code is not clear as we don't know what is your env object as all your class are inner classes.
It looks like you need to properly implement onMeasure so the scrollview knows how tall that view is.
I'm trying to add multiple customo views (for now they are simple rectangles) using this code
//Defining the layout
HandLayout = new LinearLayout(this);
HandLayout.setOrientation(LinearLayout.HORIZONTAL);
HandLayout.setBackgroundColor(getResources().getColor(R.color.bg_hand));
HandLayout.setLayoutParams(new LayoutParams(getResources().getInteger(R.integer.dim_hand_width), getResources().getInteger(R.integer.dim_hand_height)));
//Some more code
int dx = getResources().getInteger(R.integer.dim_cardslot_width);
for (int i = 0; i < 6; i++){
Card c = new Card(this,i);
HandLayout.addView(c);
c.setX(i*dx);
}
The problem is that instead of getting 6 rectagles one next to the other I only see the first rectangle.
I think the rectangles are there but are "behind" the first drawn rectangle. How do I tell the view to "move" them dx to the right?
Thanks for any help
EDIT: The problem is with the Card Class code, I think. After a suggestion from one of the user I've tried adding TextViews and they worked. Here is the code for the card class (NOTE the only code not present are the declarations of a bunch of constant ints).
public Card(Context context, int id) {
super(context);
// TODO Auto-generated constructor stub
borders = new Rect(0,0,
getResources().getInteger(R.integer.dim_cardslot_width),
getResources().getInteger(R.integer.dim_cardslot_height));
offw = (getResources().getInteger(R.integer.dim_cardslot_width) - getResources().getInteger(R.integer.dim_card_width))/2;
offh = (getResources().getInteger(R.integer.dim_cardslot_height) - getResources().getInteger(R.integer.dim_card_height))/2;
cborders = new RectF((float)offw, (float)offh,
(float)getResources().getInteger(R.integer.dim_card_width),
(float)getResources().getInteger(R.integer.dim_card_height));
ntext = Typeface.create(Typeface.MONOSPACE,Typeface.NORMAL);
paint = new Paint();
}
protected void onDraw(Canvas canvas){
paint.setColor(getResources().getColor(R.color.bg_card));
canvas.drawRect(borders, paint);
paint.setColor(getResources().getColor(R.color.main_card));
canvas.drawRoundRect(cborders, rad, rad, paint);
paint.setColor(getResources().getColor(R.color.card_text));
paint.setTextSize(14);
canvas.drawText("Card: " + String.valueOf(ID),offw,TopOffset+CharHeight,paint);
}
They are not "behind", they are "next" to each other, the width of every view i think is fill_parent this is why you get only one view, try to put your parent layout inside a scrollView and check the difference
XML solution:
Let's say your card design is represented as follows
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
in your java code:
for (int i = 0; i < 6; i++){
View cardView = LayoutInflater.from(getActivity()).inflate(R.layout.xml_above, null);
TextView tv = cardView.findViewById(R.id.value));
tv.setText("your text");
//if you have click listeners
tv.setOnClickListener(...);
HandLayout.addView(cardView);
}
as simple as it looks
I'm pretty much new in programing for Android. My app is sample app from api demos on developer android website. When I change parameters in that sample drawing it gets larger . That drawing needs to be displayed in scroll view (it doesn't need to be shrinked to fit the screen). This is the code I used:
DrawPoints.java
public class DrawPoints extends myActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.routes);
}
public static class SampleView extends View {
private Paint mPaint = new Paint();
private float[] mPts;
/*here comes declaration of parametars
private void buildPoints() {
/*here comes some coding*/
}
}
public SampleView(Context context, AttributeSet attributeset) {
super(context, attributeSet);
buildPoints();
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
//here also comes code
}
}
}
here is xml Code:
routes.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<HorizontalScrollView
android:id="#+id/scrollView2"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- This code is just to make sure that scroll views work how
I want them to work, image size is 625*351 px
<ImageView
android:id="#+id/image_View1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/bus_design"
/> -->
<my.package.DrawPoints.SampleView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</HorizontalScrollView>
</ScrollView>
When I run that application and push the button in main activity application crashes.
Drawing looks like Figure1 when I'm not using xml layout or scrollviews:
http://i.stack.imgur.com/za5MP.png
Figure1
I also tried to use this code after method setContentView:
View v = new SampleView(this);
addContentView(v, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
and also this one:
View v = new SampleView(this);
ScrollView.LayoutParams lp = new ScrollView.LayoutParams(1000, 1000);
addContentView(v, lp);
when I use codes, shown above, app displays Figure1 without scrollview, it seems like second content view overwrites that xml but it doesn't show correctly.
After that I tried to use this code after setContentView:
View v = new SampleView(this);
FrameLayout fl = new FrameLayout(this);
fl.findViewById(R.id.FrameLayout1);
fl.addView(v);
Frame layout (FrameLayout1) is added in routes.xml file after horizontal scroll view. When application runs I get blank screen without Figure1.
Does Anyone have an idea how to upgrade my code that would display Figure1 in ScrollView?
Thanks in advance!
write this line in your touchevent of custom View class
getParent().requestDisallowInterceptTouchEvent(true)
finally I found solution to my problem. I used solution from this site:
Link1
well I saw that solution before, but unfortunately I didn't realize that this solution is good for me. I knew that I need onMeasure method for Canvas to show it in xml, but withouth of solution from mentioned site it didn't work. Now it works. Here is my xml, and SampleView solution.
XML code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/app_layout" android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<!-- SCENE -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scene_layout"
android:drawingCacheQuality="low"
android:orientation="horizontal" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.testiranje.Kristijan.TwoDScrollView
android:id="#+id/scene_scroller" android:drawingCacheQuality="low"
android:scrollbars="horizontal"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scene_container"
android:drawingCacheQuality="low"
android:background="#drawable/map"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.testiranje.Kristijan.SampleView
android:layout_height="fill_parent"
android:layout_width="fill_parent">
</com.testiranje.Kristijan.SampleView>
<!-- <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scene_background" android:drawingCacheQuality="low"
android:background="#drawable/map"
android:layout_width="fill_parent" android:layout_height="fill_parent" /> -->
</RelativeLayout>
</com.testiranje.Kristijan.TwoDScrollView>
</RelativeLayout>
</RelativeLayout>
This is my SampleView solution:
public class SampleView extends View {
private Paint mPaint = new Paint();
private float[] mPts;
private static final float SIZE = 1000;
private static final int SEGS = 50;
private static final int X = 0;
private static final int Y = 1;
private void buildPoints() {
final int ptCount = (SEGS + 1) * 2;
mPts = new float[ptCount * 2];
float value = 0;
final float delta = SIZE / SEGS;
for (int i = 0; i <= SEGS; i++) {
mPts[i*4 + X] = SIZE - value;
mPts[i*4 + Y] = 0;
mPts[i*4 + X + 2] = 0;
mPts[i*4 + Y + 2] = value;
value += delta;
}
}
public SampleView(Context context){
super(context);
//initSampleView();
buildPoints();
}
//This constructor is very important because withouth of this
//you can't insert this view in xml
public SampleView(Context context, AttributeSet attrs) {
super(context, attrs);
//initSampleView();
buildPoints();
}
/*private final void initSampleView() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
setPadding(3, 3, 3, 3);
}*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
/**
* Determines the width of this view
* #param measureSpec A measureSpec packed into an int
* #return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
//This is because of background image in relativeLayout, which is 1000*1000px
measureSpec = 1001;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.UNSPECIFIED) {
// We were told how big to be
result = specSize;
}
return result;
}
/**
* Determines the height of this view
* #param measureSpec A measureSpec packed into an int
* #return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
//This is because of background image in relativeLayout, which is 1000*1000px
measureSpec = 1001;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.UNSPECIFIED) {
// Here we say how Heigh to be
result = specSize;
}
return result;
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
canvas.translate(10, 10);
paint.setColor(Color.RED);
paint.setStrokeWidth(0);
canvas.drawLines(mPts, paint);
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
canvas.drawPoints(mPts, paint);
}
}
Now I got this image, when I run my app:
http://i.stack.imgur.com/lWhvT.png
If anyone have questions about this feel free to ask me :).
I would know if there is the possibility to create a new Android Activity with a different layout comparing to linear layout, table layout, relative layout ect...
My goal is to create an activity where the android load some object (for example notes) and place this notes into the activity in order to give the illusion at the final user to have a lot of post-it on the screen of the tablet.
The result that i would to achieve is very similar to the layout of computer, where you have a lot of icons and you can insert, remove and move this icon that represent my notes.
I thougth to use a grid like this http://developer.android.com/guide/tutorials/views/hello-gridview.html, but with this kind of layout can i move where i want my icons??
Something like this:
public class MyActivity extends Activity {
private RelativeLayout _mainLayout;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_mainLayout = (RelativeLayout) findViewById(R.id.canvas);
addChild(50, 50, 150, 150, Color.RED);
addChild(150, 250, 50, 50, Color.GREEN);
addChild(200, 200, 100, 100, Color.BLUE);
}
public void addChild(int centerX, int centerY, int width, int height, int color) {
int X = centerX - width / 2;
int Y = centerY - height / 2;
RelativeLayout child = new RelativeLayout(this);
child.setBackgroundColor(color);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
layoutParams.leftMargin = X;
layoutParams.topMargin = Y;
layoutParams.bottomMargin = -250;
layoutParams.rightMargin = -250;
child.setLayoutParams(layoutParams);
_mainLayout.addView(child);
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:id="#+id/canvas"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/bg0large"
>
</RelativeLayout>
You would need to draw your views manually to the screen, probably using something like a Canvas. This will allow you to draw yours views where you like.
I have a class Ball which extents View.Inside there i give some characteristics and implements onTouchEvent() so i can handle the movement.Also i use onDraw so i can draw the bitmap of the ball. in my activity class i crate a new Layout and i add the view to it so it can be displayed. Everything works fine except when, i try to add more balls to my layout they don't appear!Always the first added in layout ball is displayed!
Here's the onCreate code from activity class:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
int lHeight = LinearLayout.LayoutParams.WRAP_CONTENT;
int lWidth = LinearLayout.LayoutParams.WRAP_CONTENT;
Point point1 = new Point();
point1.x = 50;
point1.y = 20;
Point point2 = new Point();
point2.x = 100;
point2.y = 20;
Point point3 = new Point();
point3.x = 150;
point3.y = 20;
ColorBall ball1 = new ColorBall(this,R.drawable.bol_groen, point1);
ll.addView(ball1, new LinearLayout.LayoutParams(lHeight, lWidth));
setContentView(ll);
ColorBall ball2 = new ColorBall(this,R.drawable.bol_rood, point2);
ll.addView(ball2, new LinearLayout.LayoutParams(lHeight, lWidth));
setContentView(ll);
ColorBall ball3 = new ColorBall(this,R.drawable.bol_blauw, point3);
ll.addView(ball3, new LinearLayout.LayoutParams(lHeight, lWidth));
setContentView(ll);
}
What could possibly be the problem?I have try it also with only one setContentView() at the end.I am thinking that i can't use a Layout so i can draw bitmaps which are in a custom View!Am i right?
Should i change my code and create a View and inside there make an array with all the balls i want to be displayed and then set this View to be displayed from my main class of activity?(like this setContentView(customview)).
You are calling setContentView several times, while it is expected to call only once per activity initialization.
UPDATE:
Could you use this layout xml instead of programmatic way you use? This is just to be 100% sure the container to which you will add the ColorBalls is Ok.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Just in case, here is the code to include it in the activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_name_of_layout);
LinearLayout container = (LinearLayout) findViewById(R.id.container);
..
container.addView(ball1);
container.addView(ball2);
..
}