Android. Center text in TextView vertically - java

I have a ViewGroup class which adds a custom View and draws a pie chart on a canvas. In the center of the circle i have a TextView. But whatever i do, i cannot get the text in the TextView centered vertically.
The Root view in this case is a LinearLayout.
Snippet:
public class PieChart extends ViewGroup {
public PieChart(Context context) {
super(context);
mPieView = new PieView(getContext());
addView(mPieView);
}
...
private class PieView extends View {
public PieView(Context context) {
super(context);
text = new TextView(getContext());
text.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.FILL_PARENT));
text.setHeight(400);
text.setText(getCenterTextValue());
text.setTextColor(Color.BLACK);
text.setGravity(Gravity.CENTER);
text.setBackgroundColor(Color.GRAY);
addView(text);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
text.layout((int)mBoundsInner.left, (int)mBoundsInner.top, (int)mBoundsInner.right, (int)mBoundsInner.bottom);
}
}
...
}
This is how it looks like in the emulator. I have set a grey background for demonstration purposes so that you can see the bounds of the TextView
Picture of how it looks like in android emulator
Here is the activity.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ls="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:orientation="vertical" >
<com.lifescraper.view.PieChart
android:id="#+id/Pie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="0dip">
</com.lifescraper.view.PieChart>
</LinearLayout>

Related

How do I guarantee that my Android SurfaceView is transparent instead of black?

I have a custom view that extends SurfaceView overlaying the rest of my interface, it works on emulators and when the debugger is connected on my phone, but but when the phone is running on battery the view never clears.
public class CustomView extends SurfaceView {
private final Paint paint;
private final SurfaceHolder holder;
private final Context context;
private float strokeWidth = 4;
private boolean canvasAlreadyLocked = false;
public CustomView(Context viewContext, AttributeSet attrs)
{
super(viewContext, attrs);
Log.i("CustomView", "CustomView create context & attrs");
holder = getHolder();
context = viewContext;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
drawLine();
setZOrderOnTop(true);
holder.setFormat(PixelFormat.TRANSPARENT);
}
public void resume() {
Log.i("CustomView", "Resume the customview display.");
setZOrderOnTop(true);
holder.setFormat(PixelFormat.TRANSPARENT);
}
#Override
public void onAttachedToWindow(){
super.onAttachedToWindow();
setZOrderOnTop(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
protected void drawLine() {
if (!canvasAlreadyLocked) {
invalidate();
if (holder.getSurface().isValid()) {
try {
final Canvas canvas = holder.lockCanvas();
canvasAlreadyLocked = true;
if (canvas != null) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(strokeWidth * 2);
canvas.drawLine(0, getY(), getWidth(), getY(), paint);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(strokeWidth);
canvas.drawLine(0, getY(), getWidth(), getY(), paint);
holder.unlockCanvasAndPost(canvas);
canvasAlreadyLocked = false;
}
}
catch (IllegalArgumentException iae)
{
Log.w("CustomView", "Exception trying to lock canvas: "+iae.getMessage());
Log.getStackTraceString(iae);
}
}
}
}
private float getY() {
getHeight()/2;
}
}
I'm aware that some of the calls here are redundant - that is mostly the legacy of trying lots of different things to try to make it work. You will notice that I have already done everything recommended in this answer.
The layout works like this:
<FrameLayout 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.custom.CustomViewApp">
<FrameLayout
android:id="#+id/control"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true">
<com.custom.AutoFitTextureView
android:id="#+id/texture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<ImageButton
android:id="#+id/gpsNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/gps_unfixed"
android:layout_gravity="right"
android:tint="#color/gps_unfixed"
android:background="#null" />
<ProgressBar
android:id="#+id/camera_spinner"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:gravity="center"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="invisible"
/>
<com.custom.CustomView
android:id="#+id/custom_view"
android:background="#color/transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
</FrameLayout>
</FrameLayout>
This is pulled in from a ViewFragment:
#Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);
gpsNotification = (ImageButton) view.findViewById(R.id.gpsNotification);
customView = (CustomView) view.findViewById(R.id.custom_view);
spinner = (ProgressBar) view.findViewById(R.id.camera_spinner);
spinner.setVisibility(VISIBLE);
}
I have tried to simplify this as far as I can and obviously there is a lot more happening in this scenario, but hopefully this is enough to indicate where the problem might be coming from.
The AutoFitTextureView is displaying the view from the camera.
When I run it in an emulator, everything displays as expected,
regardless of the battery settings.
When I run it on my phone connected by USB everything displays as expected.
When I run it on my phone disconnected, the view will usually, but not always, show as plain black - the AutoFitTextureView is completely obscured, but the line on the CustomView is drawn. The other components are visible, which leads me to suspect there is a problem with when or how I call setZOrderOnTop().
If I hit a breakpoint in the fragment and set the visibility of the CustomView to INVISIBLE I can immediately see everything else, but as you might expect I lose the overlay. Manually calling setZOrderOnTop at that point doesn't seem to change anything.
When it is running correctly, I can examine the structure with the Layout Inspector tool, but when the SurfaceView is opaque the Layout Inspector raises an Unexpected Error: Empty View Hierarchy message. I haven't been able to locate a corresponding error message in Logcat.
How do I ensure my SurfaceView-derived View is always transparent? If I can't guarantee that, is there any way I can test whether it currently is transparent so that I can intercede if it is not?
I think I have now solved this and the problem was that I was trying to put a SurfaceView derived type over a TextureView derived type. Switching out the AutofitTextureView for another SurfaceView showing the camera preview seems to have done the trick.

Scale animation of the custom view (relative to the centre point)

I have a custom view, which I want to scale relative to the centre.
If I use this line of code for the ImageView it works perfectly well and scale correctly:
imageView.animate().scaleX(2.5f).scaleY(2.5f).setDuration(2000);
But if I use it for my custom view, it goes up, and animation doesn't look correctly, how to fix it?
Here is a video: https://youtu.be/f0-jMqE9ULU
(The animation of the red circle going wrong, animation of the pink circle (imageView) works correctly)
My custom view:
public class CircleDrawView extends View {
private Paint paint;
private int x;
private int y;
private String labelName;
private int radius = 40;
public CircleDrawView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public CircleDrawView(Context context)
{
super(context);
paint = new Paint();
}
public CircleDrawView(Context context, int x, int y, String labelName)
{
super(context);
paint = new Paint();
this.x=x;
this.y=y;
this.labelName=labelName;
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.RED);
canvas.drawCircle(x, y, radius, paint);
Paint textPaint = new Paint();
textPaint.setTextSize(25);
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
Rect bounds = new Rect();
textPaint.getTextBounds(labelName, 0, labelName.length(), bounds);
canvas.drawText(labelName, x, y, textPaint);
}
}
Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout rootView =(LinearLayout) findViewById(R.id.test_layout);
ImageView imageView = (ImageView) findViewById(R.id.test_image);
CircleDrawView circle = new CircleDrawView(getApplicationContext(), 200, 200, "1");
rootView.addView(circle);
rootView.invalidate();
circle.animate().scaleX(1.2f).scaleY(1.2f).setDuration(2000);
imageView.animate().scaleX(2.5f).scaleY(2.5f).setDuration(2000);
}
}
xml:
<?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.attracti.animation.MainActivity">
<LinearLayout
android:id="#+id/test_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/test_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="100dp"
android:background="#drawable/circle_shape" />
</LinearLayout>
</RelativeLayout>
Your call to circle.animate() returns a ViewPropertyAnimator.
The scaleX() and scaleY() methods you call on this ViewPropertyAnimator scale the View about it's pivot.
The pivot of a View is set by the View.setPivotX() and View.setPivotY() methods.
In your constructor, set these values to the supplied x and y. Your view will then scale about the supplied center point.

popup window over canvas Android

I am building an application which display a map (the map is the canvas background) and localise users by adding circle on the canvas(using draw circle). I would like to add a button over the canvas(for now a draw a button on the map and check with ontouch() if the user clicked on it) and when the button is touched I would like to have a window popup. It worked but the popup is behind the canvas(I could see a small piece of it(I removed it)).Is there a way to have my canvas BEHIND the button and the popup window? I saw people talking about putting the canvas in relative layout but I have no idea how to do that.
Here is the xml of my activity, really simple:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Button" />
</RelativeLayout>
And here is my activity java code(I removed a couple of things that doesnt have nothing to do with my problem)
package com.example.client;
import java.util.LinkedList;
//....
import java.util.Timer;
public class Campus extends Activity{
final Handler myHandler = new Handler();
MapView mapv;
final Activity self = this;
Float ratioX;
Float ratioY;
int width;
int height;
static boolean out=false;
Intent i;
//creating a linked list for each hall
static LinkedList<compMac> DMS = new LinkedList<compMac>();
static LinkedList<compMac> MCD = new LinkedList<compMac>();
//...
static LinkedList<compMac> SCI = new LinkedList<compMac>();
static LinkedList<compMac> STE = new LinkedList<compMac>();
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.campus);
setSize();
this.mapv = new MapView(this);//!! my view
setContentView(mapv);
i= new Intent(this, myService.class);
this.startService(i);
}
//*******************************View class!*******************************
public class MapView extends View {
/*
* Extract the connected users and location from the array. separate the
* array into an array for each building
* */
private Paint redPaint;
private float radius;
Canvas canvas;
public MapView(Context context) {
super(context) ;
redPaint = new Paint();
redPaint.setAntiAlias(true);
redPaint.setColor(Color.RED);
redPaint.setTextSize(10);
}
#Override
//drawing a point on every hall on the map where users are connected
public void onDraw (Canvas canvas) {
// draw your circle on the canvas
if(!out)
{
AlertDialog.Builder outOfCampus = new AlertDialog.Builder(self);
outOfCampus.setTitle("Sorry");
outOfCampus.setMessage("You are out of Campus");//(toDisplay);
outOfCampus.setCancelable(false);
outOfCampus.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
startActivity(new Intent("com.example.client.Sin"));
}});
AlertDialog alertdialog = outOfCampus.create();
outOfCampus.show();
}
this.canvas=canvas;
setBackgroundResource(R.drawable.umap2);
}
public void drawPoints(LinkedList<compMac> building)
{
if(!building.isEmpty())
{
while(!building.isEmpty())
{
compMac current = building.pop();
Float x= ratioX*(Float.parseFloat(current.getCoorX()));
Float y= ratioY*(Float.parseFloat(current.getCoorY()));
// Log.w("ratioX ",(((Double)(width/768)).toString()));
// Log.w("ratioY ",(float)(y.toString()));
canvas.drawCircle (x,y, 10, redPaint);
}
}
}
public boolean onTouchEvent (MotionEvent event) {
//...//
return true;
}
}
}
Someone have an idea how i can do that? Thanks
Calling setContentView two times would not work. Instead you should put your canvas view and the button in a single layout itself but with proper ordering. The last widget in the relative layout gets more priority, so if you want the button to come on top of the canvas your layout should be something like this.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.client.MapView
android:id="#+id/mapView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Button" />
</RelativeLayout>
And to access your MapView in java class
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.campus);
setSize();
this.mapv = findViewById(R.id.mapView); //!! my view
i= new Intent(this, myService.class);
this.startService(i);
}
And obviously alert dialog will be on top of the canvas. Hope it helps!
Edit: I think inflate error is due to incorrect class path. Since MapView is inner class of Campus, path should be like this
<com.example.client.Campus.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Also add this constructor to your MapView class
public MapView(Context context, AttributeSet attributeSet) {
super(context, attributeSet) ;
redPaint = new Paint();
redPaint.setAntiAlias(true);
redPaint.setColor(Color.RED);
redPaint.setTextSize(10);
}
<FrameLayout 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" >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#drawable/umap2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/btn_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:background="#drawable/back_transparent_pressed" >
<Button
android:id="#+id/button1"
android:layout_centerInParent="true"
/>
</RelativeLayout>

Android : custom Drawable for the layout background . Child items are not clear?

My layout file is like this :
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="282dp"
android:layout_height="wrap_content"
android:layout_x="20dp"
android:layout_y="208dp"
android:alpha="155"
android:orientation="vertical" >
<EditText
android:id="#+id/txtUserName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="155"
android:hint="#string/hintUsername"
android:padding="2dp" >
<requestFocus />
</EditText>
<EditText
android:id="#+id/txtPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="155"
android:hint="#string/hintPassword"
android:inputType="textPassword"
android:padding="2dp" />
<Button
android:id="#+id/butBrowse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="255"
android:text="#string/but_browse" />
</LinearLayout>
And I set a custom Drawable to the layout programatically as follows :
ViewGroup layoutView = (ViewGroup) getLayoutInflater().inflate(
R.layout.login_layout, null); // (ViewGroup)
layoutView.setBackground(new CustomDrawable(this) {
});
private class CustomDrawable extends Drawable {
private Context ctx;
private Bitmap bitmap;
public CustomDrawable(Context ctx) {
this.ctx = ctx;
init();
}
private void init() {
//draw dynamic stuff to the 'bitmap'
}
#Override
public void draw(Canvas canvas) {
canvas.save();
Paint p = new Paint();
canvas.drawBitmap(this.bitmap,0,0,p);
canvas.restore();
}
#Override
public int getOpacity() {
return 15;
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
}
Now my login_layout looks like this:
As can be seen in the picture , my child elements (edittext,password area, button) are not clearly visible.
My questions :
1.How to achieve something like above ?Do I have to apply another Drawable to the inner 'LinearLayout' ?
2.Is it possible to change the alpha level of the Textfields,Button so that it is more visible ?
you need to change the opacity for your custom drawable which is currently set to 15. Increasing the opacity will make it more visible.
#Override
public int getOpacity() {
return 15; // increase this value
}

ImageView doesn't wrap content in RelativeLayout

I have a TableLayout with 6 childs/entrys. These childs are a custom RelativeLayout. In each RelativeLayout is a big TextView in the middle and an ImageView and small TextView at the bottom.
The ImageView should be as tall as the TextView next to it. That's why I set the attribute ALIGN_TOP and ALIGN_BOTTOM to the TextView (you can see it in code below). This works very well and both - ImageView and TextView - have the same height now. But the problem is, that the left and right side of the ImageView don't "wrap content" anymore (as you can see on the screenshot).
Is there a way to fit the left and right side to the image and remove the "padding"?
Here is my code:
view_display_component.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="#+id/tvDisplayBig"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_weight="1"
android:gravity="center"
android:textColor="#color/white"
android:textSize="#dimen/font_size_extra_large" />
<ImageView
android:id="#+id/imageViewDisplayIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/tvDisplayBig"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:baselineAlignBottom="true"
android:scaleType="fitCenter"
android:src="#drawable/stopwatch_64"
android:visibility="visible" />
<TextView
android:id="#+id/tvDisplaySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom"
android:includeFontPadding="false"
android:textColor="#color/white"
android:textSize="#dimen/font_size_small" />
</merge>
class DisplayComponent which extends RelativLayout
public DisplayComponent(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_display_component, this, true);
tvDisplay = (TextView) getChildAt(0);
icon = (ImageView) getChildAt(1);
tvName = (TextView) getChildAt(2);
setupAlign();
}
private void setupAlign() {
if(index % 2 == 0) { // LEFT SIDE
// same as "RIGHT SIDE"
} else { // RIGHT SIDE
RelativeLayout.LayoutParams paramsIcon = (RelativeLayout.LayoutParams) icon.getLayoutParams();
paramsIcon.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
paramsIcon.addRule(RelativeLayout.ALIGN_TOP, tvName.getId());
paramsIcon.addRule(RelativeLayout.ALIGN_BOTTOM, tvName.getId());
icon.setLayoutParams(paramsIcon);
RelativeLayout.LayoutParams paramsTvName = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
paramsTvName.addRule(RelativeLayout.RIGHT_OF, icon.getId());
tvName.setLayoutParams(paramsTvName);
tvName.setBackgroundColor(Color.BLUE); // only for testing
icon.setBackgroundColor(Color.YELLOW);
}
I found an (ugly) solution. Because my icon is square, I created a custom ImageView and overrode the onSizeChanged() method like this:
public class IconImageView extends ImageView {
public IconImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(h != oldh && h > 0)
getLayoutParams().width = h; // same width as height
}
}
But this works only if the image is square. That's why I am still searching for a better solution. Maybe some layout solution with a better setting of alignment.
Best regards!

Categories

Resources