Button drawing wrongly background after text size changed - android

I am putting a bunch of buttons in LinearLayout, with weight=1 for each. The problem is, that when the setTextSize gets called, it sometimes changes the space the background is drawn on (so if the new text size is smaller the one button has perceived height FE 2/3 of all the other buttons). The onSizeChanged gets never called even though the size drawn changes. The button does not change it's postion (debug by getLocationInWindow) neither it's size (getWidth, getHeight).
Am I doing something wrong, or is there at least a way to fix it?
MainActivity:
import android.app.ActionBar;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup mainLayout = (ViewGroup)findViewById(R.id.mainLayout);
LinearLayout l = new LinearLayout(this);
mainLayout.addView(l);
for (int i = 0; i < 8; i++) {
Button b = new Button(this);
LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, 200);
p1.weight = 1;
b.setLayoutParams(p1);
b.setBackgroundColor(Color.YELLOW);
l.addView(b);
}
Button b = ((Button)l.getChildAt(7));
b.setTextSize(50f);
}
}
And for it to be all, XML:
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="dracek.fit.cvut.cz.test.MainActivity">
</android.support.constraint.ConstraintLayout>
I tried all this for the button but no change:
requestLayout();
invalidate();
ViewParent vp = getParent();
if (vp != null)
vp.requestLayout();

So I will answer myself if noone else does so.
In the end I found out, that the LinearLayout is for whatever reason trying to have all the text baselines in the same height, thus moving the buttons. Adding one line solves the problem:
p1.gravity =Gravity.FILL;

Related

Moving a button (onClick) to a random position on the screen

everyone. I'm sort of new to android development and I've ran into a bit of a problem.
As is it's written in the title, I'm trying to create an app in which a button moves to a random location on the screen every time it is clicked. A sort of a "runaway button" thingy.
Here is the XML code.
<?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:id="#+id/activity_main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:background="#color/colorPrimary"
tools:context="com.example.a4ld.MainActivity">
<Button
android:id="#+id/button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button" />
</LinearLayout>
And here is the main activity code.
At first the solution seems pretty simple. Just move the random values into the onClick method. That way new coordinates would get generated every time the button is clicked. But whenever I do that the animationListener class cannot reach those values and move the view accordingly so that the clickable area of the button would match its position on the screen.
package com.example.a4ld;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.LinearLayout;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//get display size.
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
final int height = displayMetrics.heightPixels;
final int width = displayMetrics.widthPixels;
//generates random values within display size range.
//doesn't work as intended. the button still goes out of bounds sometimes.
Random n = new Random();
final int height01 = n.nextInt(height - 0) + 0;
Random m = new Random();
final int width01 = m.nextInt(width - 0) + 0;
final Button button01 = (Button) findViewById(R.id.button01);
button01.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//animates button movement.
TranslateAnimation animation01 = new TranslateAnimation(0, width01, 0, height01);
animation01.setDuration(500);
animation01.setAnimationListener(new animationListener());
button01.startAnimation(animation01);
}
class animationListener implements Animation.AnimationListener {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
//moves the view(probably) to match the new button position.
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(button01.getWidth(), button01.getHeight());
layoutParams.setMargins(width01, height01, 0, 0);
button01.setLayoutParams(layoutParams);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
});
}
}
Pardon me for any mistakes. This is my first time asking for help here.
Every bit of help will be greatly appreciated and thank you for your time.
You should rather use ViewPropertyAnimator. This animates the view to its future position and you don't need to force any layout params on the view after the animation ends. And it's rather simple.
myView.animate().x(50f).y(100f);
myView.animate().translateX(pixelInScreen)
Note: This pixel is not relative to the view. This pixel is the pixel position in the screen.

Text and line drawn on ToggleButton canvas appear on screen but not on screenshot

I have created a view MyToggleButton that extends ToogleButton. I overwrite the Togglebutton's onDraw method to draw a line on the button and to rotate it's text.
Here is MyToggleButton:
package com.example.gl.myapplication;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.widget.ToggleButton;
public class MyToggleButton extends ToggleButton {
public float textRotation=0;
public MyToggleButton(Context context, float rotationValue) {
super(context);
textRotation=rotationValue;
}
#Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
//paint
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(7);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawLine(canvas.getWidth() / 2, canvas.getHeight() / 2, (float) (canvas.getHeight() / 2 + 400), (canvas.getHeight() / 2), paint);
canvas.rotate(textRotation, canvas.getWidth() / 2, canvas.getHeight() / 2);
getLayout().draw(canvas);
canvas.restore();
}
}
After that I want to get a screenshot of and view it and for that I use the takeScreenshot() method that is called by pressing the 3rd button on screen. The screenshot code is based on How to programmatically take a screenshot in Android?. The same approach is followed in other posts on stackoverflow, and in various sites and tutorials I found around.
Here is the MainActivity that I use:
package com.example.gl.myapplication;
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.PorterDuffXfermode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//add 3 buttons
addButtons();
}
private void addButtons(){
LinearLayout linear1= (LinearLayout) findViewById(R.id.vert1);
//MyToggleButton with rotated text
MyToggleButton btn1 = new MyToggleButton(this,0);
btn1.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
btn1.setTextOff("Button 1 not rotated");
btn1.setTextOn("Button 1 not rotated");
btn1.setText("Button 1 not rotated");
linear1.addView(btn1);
//MyToggleButton with normal text
MyToggleButton btn2 = new MyToggleButton(this,50);
btn2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
btn2.setTextOff("Button 2 rotated");
btn2.setTextOn("Button 2 rotated");
btn2.setText("Button 2 rotated");
linear1.addView(btn2);
//button to create screenshot
Button btn3 = new Button(this);
btn3.setText("Create bitmap");
btn3.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
takeScreenshot();
}
});
linear1.addView(btn3);
}
//Screenshotcreator
private void takeScreenshot() {
try {
View v1 = getWindow().getDecorView().getRootView();
// create bitmap screen capture
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
ImageView imageView1 =(ImageView) findViewById(R.id.imageView1);
imageView1.setImageBitmap(bitmap);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Here is the layout of MainActivity:
<?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="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.gl.myapplication.MainActivity"
android:id="#+id/relative1">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/vert1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="250dp"
android:id="#+id/imageView1" />
</LinearLayout>
</RelativeLayout>
The problem is that although everything looks ok on the screen, the screenshot's bitmap lacks the drawn line and the rotated text.
What could be the reason for that strange result in the bitmap?
Notice that that the devices back-home-recents buttons are missing from the screenshot, as also the status bar information (time etc). I doubt that this has to do something with my issue. Those elements do not belong in my app so it is expected for them to not appear in the view I get with get with v1 = getWindow().getDecorView().getRootView(); But the canvas on which I draw the rotated text and the line do belong to that view.
My question is: Why does the drawn line and the rotated text do not appear on the screenshot, and how can I modify my code to get a screenshot with them on too?
I don't want to do something that needs a rooted device and also I cannot use MediaProjection API because this was added in API 21 but I want my app to run on android 4.4 (API 19) too. Finally, I am aware of the existence of external libraries such as Android Screenshot Library or this: https://android-arsenal.com/details/1/2793 , but I would rather not use any external library.
P.S. maybe this is related:Surfaceview screenshot with line/ellipse drawn over it
The problem is that both btn1.isDrawingChaceEnabled() and btn2.isDrawingChaceEnabled() return false.
Even if you call setDrawingCacheEnabled(true) on the RootView, it is not set as true recursively on all his Children View (I tested it with Log.d).
That means that the drawing cache of your ToggleButton will be empty as explained in the android.view.View.getDrawingCache() javadoc.
/**
* <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
*
* #return A non-scaled bitmap representing this view or null if cache is disabled.
*
* #see #getDrawingCache(boolean)
*/
So make sure you set setDrawingCacheEnabled(true) for each of the views you are interested in.
For example I added the following lines to your code:
in MainActivity
btn1.setDrawingCacheEnabled(true);
btn2.setDrawingCacheEnabled(true);
And it looks like this

Drawing shapes programmatically for Android

I'm new to the Android SDK so I'm trying to figure this out. I have read the documentation and a text book and they haven't been particularly helpful in this matter.
I'm just trying to draw a simple rectangle in a linear layout on the screen. I can't get the shape to show up, however, when I add text to this layout in the same fashion, the text does show up. What am I missing?
package jorge.jorge.jorge;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ShapesActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ShapeDrawable rect = new ShapeDrawable(new RectShape());
rect.getPaint().setColor(Color.GREEN);
ImageView view1 = new ImageView(this);
view1.setImageDrawable(rect);
LinearLayout frame = (LinearLayout)findViewById(R.id.linear1);
frame.addView(view1);
// TextView tx = new TextView(this);
//
// tx.setText("Hello World");
//
// frame.addView(tx);
}
}
The Shape is usually used for making a background to some View. Its width and height is the same of the view that is using it. Then, if this view has no width and height, It'll have no width and height, too.
Basically, I think that your ImageView has no width and height, then it's invisible.
You can see how to set it programatically here:
Set ImageView width and height programmatically?
But, I recomend you to make the layout in XML's way.

Android, RelativeLayout restarts Marquee-TextView when changing ImageView in same RelativeLayout

I have not found a solution for my problem, maybe you can help me here.
I am using a RelativeLayout with an ImageView and a TextView as children. The TextView contains a large text and should scroll from right to left. But everytime when I set a new image to the ImageView, the marquee starts from beginning.
I think that by setting a new image the TextView looses its focus and so the marquee starts again. How can I prevent that or is there something else I am doing wrong? Would be great if someone could point me to the correct solution.
Thanks a lot!
You can reproduce my problem with this code:
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils.TruncateAt;
import android.view.Display;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class RelativeLayoutActivity extends Activity {
private ImageView mImageView;
private TextView mTextView;
private Handler mHandler = new Handler();
private int mCurrentImage = 0;
private Runnable mCallback = new Runnable() {
#Override
public void run() {
toggleImage();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initLayout();
setLongText();
mHandler.postDelayed(mCallback, 5000);
}
private void initLayout() {
RelativeLayout layout = (RelativeLayout) findViewById(R.id.parentLayout);
Display display = getWindowManager().getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
mImageView = new ImageView(this);
mImageView.setScaleType(ScaleType.FIT_XY);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(screenWidth,screenHeight);
mImageView.setImageDrawable(this.getResources().getDrawable(R.drawable.red));
layout.addView(mImageView, rlp);
// marquee text at the bottom
mTextView = new TextView(this);
mTextView.setSingleLine();
mTextView.setEllipsize(TruncateAt.MARQUEE);
mTextView.setMarqueeRepeatLimit(-1);
mTextView.setHorizontallyScrolling(true);
mTextView.setFocusable(true);
mTextView.setFocusableInTouchMode(true);
mTextView.setBackgroundColor(Color.BLACK);
mTextView.setTextColor(Color.WHITE);
rlp = new RelativeLayout.LayoutParams(screenWidth, 50);
rlp.topMargin = screenHeight-100;
layout.addView(mTextView, rlp);
}
private void setLongText() {
StringBuilder sb = new StringBuilder();
for (int i=0; i<50; i++) {
sb.append(" ");
}
for (int i=0; i<50; i++) {
sb.append("A");
}
for (int i=0; i<50; i++) {
sb.append("B");
}
for (int i=0; i<50; i++) {
sb.append("C");
}
mTextView.setText(sb.toString());
mTextView.setSelected(true);
}
private void toggleImage() {
mCurrentImage++;
if (mCurrentImage % 2 == 0) {
mImageView.setImageDrawable(this.getResources().getDrawable(R.drawable.red));
} else {
mImageView.setImageDrawable(this.getResources().getDrawable(R.drawable.green));
}
mHandler.postDelayed(mCallback, 5000);
}
}
I had a same problem and I fixed it just now:)
If your TextView which in layout XML contains layout_weight
android:layout_weight="1"
Remove it!This attibute cause marquee restart.
Hope helpful:)
The marquee reset problem is because of loss of focus of the TextView on which it is running. To overcome these issues you can simply surround your TextView with another RelativeLayout so that it is in a different ViewGroup as your other views.
You have another option: Create a new class that is a subclass of TextView and override onFocusChanged and onWindowFocusChanged to prevent loss of focus for the said textview.
That's it. Using these techniques your marquee won't restart every time another element gains focus.
See my answer here : https://stackoverflow.com/a/13841982/1823503
The idea is :
to fix the width and height programmatically (you did it)
to setSelected (you did it)
to Override your TextView to fix the focus problems
just a simple Fix..:) no need to worry much...just fix width of textview as some 800dp or too higher width. it will solve reset issue
<TextView
android:id="#+id/adv_txt_view"
android:layout_width="800dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:focusableInTouchMode="true"
android:focusable="true"
android:scrollHorizontally="true"
android:textColor="#color/black"
android:text="large text to scroll!!" />

Android Views Are Not As Tall As Specified

I am using RelativeLayout to position views at precise locations on the screen. This works as expected when using a view that say, draws a rectangle. But when using Android views like EditText, they are drawn shorter than specified by about 8 units. Clicking outside of the drawn EditText (but within the parameters specified by RelativeLayout) will, in fact, hit the EditText.
Here is some code to illustrate what I mean:
package com.DrawDemo;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout;
public class DrawDemo extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
RelativeLayout l = new RelativeLayout(this);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(100,100);
lp.leftMargin = 50;
lp.topMargin = 50;
DemoView demoview = new DemoView(this);
l.addView(demoview, lp);
EditText editText = new EditText(this);
l.addView(editText, lp);
setContentView(l);
}
private class DemoView extends View
{
public DemoView(Context context)
{
super(context);
}
#Override protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
canvas.drawPaint(paint);
}
}
}
If you execute this, you will notice that the EditText is noticeably shorter than the rectangle. I've tried mucking with onMeasure, setMinimumXXX, and just about everything else I can think of. So far, the only thing that works with some level of success is to just add 8 or so pixels to the height (8 seems to work better than trying a percentage of the height).
Next task will be to step into the source but was wondering if somebody has already solved this.
Thanks.
It's just because of the actual EditText background image that's used. Buttons are the same way, for some reason Google decided to draw the 9-patch background images with some extra transparent padding pixels. Really, the only solution, if it's a problem, would be to draw your own 9-patch, or modify the existing Android 9-patch to remove the extra pixels.

Categories

Resources