SOLVED: I fixed the problem, it had to do with the constraints in my layout.
I am trying to create a custom view that for the time being simply displays a chart. This is my custom View class:
public class ChartView extends View {
private final Paint paint;
public ChartView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
setWillNotDraw(false);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("View ", "onDraw called");
int x = getWidth();
int y = getHeight();
int divX = x / 5;
int divY = y / 7;
for (int i = 0; i <= 5; i++) {
canvas.drawLine(divX * i, 0, divX * i, y, paint);
Log.d("View", "line drawn");
}
for (int i = 0; i <= 7; i++) {
canvas.drawLine(0, divY * i, x, divY * i, paint);
Log.d("View", "line drawn");
}
}
}
At first I assumed onDraw() must not ever have been called, but I added the log messages and it was in fact being called and each line in the chart was being drawn. I also have overridden onMeausure() so I don't think thats the problem.
Just to test if anything would be drawn at all I added in a test TextView which appeared just fine. I Then found that my chartView instance in the Main activity was null because I didn't call the parent constructor properly. I fixed that though and still nothing was drawn. However, when I fixed the problem with the parent constructor, the TextView also disappeared.
This is my Main Activity:
public class MainActivity extends AppCompatActivity {
private ChartView chartView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent mainIntent = getIntent();
boolean testPassed = mainIntent.getBooleanExtra("test", false);
if (testPassed) {
Log.d("Main", "intents test passed");
}
chartView = (ChartView) findViewById(R.id.chartView);
}
and this is my layout XML file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
tools:context=".MainActivity">
<Views.ChartView
android:id="#+id/chartView"
android:layout_width="425dp"
android:layout_height="634dp"
android:layout_marginEnd="601dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/textView"
android:layout_width="260dp"
android:layout_height="292dp"
android:layout_marginTop="256dp"
android:text="TextView"
android:textSize="100dp"
app:layout_constraintBottom_toTopOf="#+id/chartView"
app:layout_constraintEnd_toStartOf="#+id/chartView"
app:layout_constraintHorizontal_bias="0.582"
app:layout_constraintStart_toEndOf="#+id/chartView"
app:layout_constraintTop_toBottomOf="#+id/chartView"
app:layout_constraintVertical_bias="0.801" />
</androidx.constraintlayout.widget.ConstraintLayout>
Any help would be appreciated.
Related
This will be my first question, so I apologize for my probable mistakes.
I'm trying to add a red circle each time I press the button I've incorported to my layout. I'd like all circles stayed in the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/panelJuego"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.76"
android:orientation="horizontal" >
</LinearLayout>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="botonRojo"
android:text="Button" />
</LinearLayout>
The java code related with my trying is:
public void botonRojo(View v) {
LinearLayout panelJuego = (LinearLayout) findViewById(R.id.panelJuego);
PonCirculo circulo = new PonCirculo(this, 30, 30, "#FF0000");
circulo.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
panelJuego.addView(circulo);
}
public class PonCirculo extends View {
private int radio = 30;
private String color;
public PonCirculo(Context context, int x, int y, String color) {
super(context);
Cx = Cx + x;
Cy = Cy + y;
this.color = color;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor(color));
canvas.drawCircle(Cx, Cy, radio, paint);
}
Actually each time I press the button a red filled circle appears in the android screen but when I press the button again a new circle appears and the fomer disappears. Can anyone help me? Thanks.
I'm assuming you are just trying to add each circle under each other when the button is pressed.
Currently you are adding a new view each time now but it is over-laying the previous view. You need add the views giving each new circle a unique id. Then place the new circle underneath or whatever position you choose. I'm giving you some example code that I changed to use a relative layout and I place each circle under the previous one. This should get you started with what you are looking for:
Example Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/relativeCircleLayout">
</RelativeLayout>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Circle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:onClick="botonRojo"/>
</RelativeLayout>
Example Code:
public class MainActivity extends AppCompatActivity {
ArrayList<PonCirculo> viewList = new ArrayList<PonCirculo>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void botonRojo(View v) {
RelativeLayout panelJuego = (RelativeLayout) findViewById(R.id.relativeCircleLayout);
PonCirculo circulo = new PonCirculo(this, 30, 30, "#FF0000");
circulo.setId(View.generateViewId());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
if(!viewList.isEmpty()) {
int id = viewList.get(viewList.size()-1).getId();
params.addRule(RelativeLayout.BELOW, id);
}
panelJuego.addView(circulo, params);
viewList.add(circulo);
}
public class PonCirculo extends View {
private int radio = 30;
private String color;
int Cx, Cy;
public PonCirculo(Context context, int x, int y, String color) {
super(context);
Cx = Cx + x;
Cy = Cy + y;
this.color = color;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor(color));
canvas.drawCircle(Cx, Cy, radio, paint);
}
}
}
Background
I have a custom ViewGroup subclass that rotates and mirrors its child view. The purpose for this is to correctly display traditional Mongolian text.
I could put anything is this ViewGroup, but for my current project I am putting an EditText in it. (I was never successful in just rotating and mirroring the EditText directly. However, wrapping it in this custom view group does work.)
Problem
My problem is that when I try to resize the ViewGroup programmatically, its child view is not getting resized properly along with it. I would like the EditText to match the size of the parent ViewGroup so that it appears to be a single view.
MCVE
I made a new project to show the problem. The button increases the width of the ViewGroup (shown in red). The images show the project start (with everything working fine) and two width increments. The EditText is white and is not getting resized even though the width and height are set to match_parent
The full project code is below.
MongolViewGroup.java (Custom ViewGroup that rotates and mirrors its content)
public class MongolViewGroup extends ViewGroup {
private int angle = 90;
private final Matrix rotateMatrix = new Matrix();
private final Rect viewRectRotated = new Rect();
private final RectF tempRectF1 = new RectF();
private final RectF tempRectF2 = new RectF();
private final float[] viewTouchPoint = new float[2];
private final float[] childTouchPoint = new float[2];
private boolean angleChanged = true;
public MongolViewGroup(Context context) {
this(context, null);
}
public MongolViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public View getView() {
return getChildAt(0);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View view = getView();
if (view != null) {
measureChild(view, heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(resolveSize(view.getMeasuredHeight(), widthMeasureSpec),
resolveSize(view.getMeasuredWidth(), heightMeasureSpec));
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (angleChanged) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
angleChanged = false;
}
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.rotate(-angle, getWidth() / 2f, getHeight() / 2f);
canvas.scale(-1, 1);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
invalidate();
return super.invalidateChildInParent(location, dirty);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
viewTouchPoint[0] = event.getX();
viewTouchPoint[1] = event.getY();
rotateMatrix.mapPoints(childTouchPoint, viewTouchPoint);
event.setLocation(childTouchPoint[0], childTouchPoint[1]);
boolean result = super.dispatchTouchEvent(event);
event.setLocation(viewTouchPoint[0], viewTouchPoint[1]);
return result;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
MongolViewGroup viewGroup;
EditText editText;
int newWidth = 300;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewGroup = (MongolViewGroup) findViewById(R.id.viewGroup);
editText = (EditText) findViewById(R.id.editText);
}
public void buttonClicked(View view) {
newWidth += 200;
ViewGroup.LayoutParams params = viewGroup.getLayoutParams();
params.width=newWidth;
viewGroup.setLayoutParams(params);
}
}
activity_main.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:id="#+id/activity_main"
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.mongolviewgrouptest.MainActivity">
<com.example.mongolviewgrouptest.MongolViewGroup
android:id="#+id/viewGroup"
android:layout_width="100dp"
android:layout_height="200dp"
android:background="#color/colorAccent">
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#android:color/black"
android:background="#android:color/white"/>
</com.example.mongolviewgrouptest.MongolViewGroup>
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:onClick="buttonClicked"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
You're not recalculating viewRectRotated for your EditText when the
ViewGroup's onLayout(...) method is called again.
Since angleChanged is set to false (and never changes) after your ViewGroups first layout, then the part that calculates the left, right, top and bottom values for your EditText
is skipped any time after the first time when your ViewGroup
requestsLayout (when you change its height or width).
As such, your EditText is still laid out with the same left,right,top
and bottom values it was initially laid out with.
Do away with the angleChanged and it should work just fine. Like so:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
I've tested this and it works just fine this way.
If you need angleChanged for any reason, then just make sure it's changed back to true inside your ViewGroup's onMeasure method so that viewRectRotated is recalculated again. However I wouldn't recommend that.
I am using a custom edittext but the problem is that I am unable to set a text for my custom edittext. Here is what all I tried from the available answers on SO,
setText not working for a Custom Edittext
This did not work still. I did not get any error,so no clued why it did not work.
Custom TextView - setText() called before constructor
This also did not work.
XML FILE
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="#FFFFFF"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin" >
<com.random.practiceproject.LinedEditText
android:layout_width="301dp"
android:inputType="text"
android:layout_height="match_parent" />
</LinearLayout>
MainActivity
LinedEditText lt;
EditText et; //Normal edittext works
String s = "This is a sample string";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lt = new LinedEditText(this);
et = (EditText) findViewById(R.id.newt);
lt.setCursorVisible(true);
lt.setTextColor(Color.BLACK);
lt.setText(s,TextView.BufferType.EDITABLE);
et.setText(s, TextView.BufferType.EDITABLE);
}
Here is the code,
public class LinedEditText extends EditText {
private static Paint linePaint;
static {
linePaint = new Paint();
linePaint.setColor(Color.BLACK);
linePaint.setStyle(Paint.Style.STROKE);
}
public LinedEditText(Context context)
{
super(context);
}
public LinedEditText(Context context, AttributeSet attributes) {
super(context, attributes);
}
#Override
protected void onDraw(Canvas canvas) {
Rect bounds = new Rect();
int firstLineY = getLineBounds(0, bounds);
/*int firstLineY=0;
for(int i =0;i<getLineCount();i++)
{
firstLineY = (i + 1) * getLineHeight();
}*/
int lineHeight = getLineHeight();
int totalLines = Math.max(getLineCount(), getHeight() / lineHeight);
for (int i = 0; i < totalLines; i++) {
int lineY = firstLineY + i * lineHeight;
canvas.drawLine(bounds.left, lineY, bounds.right, lineY, linePaint);
}
super.onDraw(canvas);
}
}
The problem is that you create new instance of LineEditText in onCreate and working with it, but not adding to layout. In the layout there is another instance of LineEditText, that you don't use.
So you must either replace:
lt = new LinedEditText(this);
with:
lt = (LinedEditText) findViewById(/*provide your id*/)
or you need to add lt to layout through ViewGroup.addView(), but I think you need to use first variant.
Here is activity and with in this activity only created one custom view
public class MainActivity extends Activity {
MyCustomDrawableView myCustomDrawableView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myCustomDrawableView = new MyCustomDrawableView(this);
setContentView(R.layout.activity_main);
myCustomDrawableView = (MyCustomDrawableView)findViewById(R.id.hello);
}
public class MyCustomDrawableView extends View {
private ShapeDrawable myDrawable;
public MyCustomDrawableView(Context context) {
super(context);
int x = 10;
int y = 10;
int width = 100;
int height = 100;
myDrawable = new ShapeDrawable(new OvalShape());
myDrawable.getPaint().setColor(0xff74fA23);
myDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
myDrawable.draw(canvas);
}
}
}
then in layout create customview as follows
<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" >
<com.mobiloitte.sampleapp.MainActivity.MyCustomDrawableView
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
here i'm getting class not found exception
10-20 13:00:33.594: E/AndroidRuntime(542): Caused by: java.lang.ClassNotFoundException: com.mobiloitte.sampleapp.MainActivity.MyCustomDrawableView
pls help
Better is to use separate class (should not be an inner class) for custom view.
For your current problem, try with
<com.mobiloitte.sampleapp.MainActivity$MyCustomDrawableView
Update for current issue android.view.InflateException
You need to add one more constructor for your custom view
public MyCustomDrawableView(Context context, AttributeSet st) {
super(context, st);
// Do other initial tasks, like you did into MyCustomDrawableView(Context context).
}
I have a ListPopupWindow that is being populated from a custom adapter. The custom xml consists of 2 textviews and a LinearLayout. The custom adapter populates the textviews and instantiates a custom view that draws a thumbnail sized vector based graphic. The custom view is then added to the Linear Layout.
The problem is that the first row displayed always has the wrong graphic displayed. It is sometimes the graphic from another row, sometimes half of it is the correct graphic, half from the wrong row. I've also noticed that if the list has enough rows in it to scroll, the items at the bottom of the list have the same problem when scrolling.
Has anyone else experienced this?
Custom List Adapter:
public class PageSearchListAdapter extends SimpleCursorAdapter
{
int _height = 130;
int _width = 130;
Cursor c;
Context context;
// Constructor, here we store any parameters we need in class variables
//
public PageSearchListAdapter(Context context,
int layout,
Cursor c,
String[] from,
int[] to)
{
super(context, layout, c, from, to);
this.c = c;
this.context=context;
}
// Main view method - the views in query_list_item are populated here
//
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// Make sure we have a view to work with
//
if(convertView == null)
convertView = View.inflate(context, R.layout.query_list_item, null);
View row = convertView;
// go to the correct row in the cursor
//
c.moveToPosition(position);
// Get the views that need populating
//
TextView PageNum = (TextView) convertView.findViewById(R.id.Page_Num);
LinearLayout pic = (LinearLayout) convertView.findViewById(R.id.Page_Canvas);
TextView Date = (TextView) convertView.findViewById(R.id.Page_Date);
// Set the values
//
PageNum.setText(c.getString(c.getColumnIndex("PageNum")));
Date.setText(c.getString(c.getColumnIndex("Timestamp")));
List<Point> Vectors = new ArrayList<Point>();
byte[] asBytes = c.getBlob(c.getColumnIndex("Vectors"));
..
.. removed code to Convert Blob to array of 'Vector' records ...
..
// Draw the page
//
NotePadPage npPage = new NotePadPage(context, Vectors);
npPage.setLayoutParams(new LayoutParams(_width, _height));
pic.addView(npPage);
return(row);
}
}
NotePadPage class:
// Adapter to display a custom list item in a list view
//
public class NotePadPage extends View
{
int _height = 130;
int _width = 130;
Bitmap _bitmap;
Canvas _canvas;
Paint _paint;
List<Point> _Vectors = new ArrayList<Point>();
public NotePadPage(Context context, List<Point> Vectors)
{
super(context);
_Vectors = Vectors;
_paint = new Paint();
_paint.setColor(Color.WHITE);
_paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
_height = View.MeasureSpec.getSize(heightMeasureSpec);
_width = View.MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(_width, _height);
_bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888);
_canvas = new Canvas(_bitmap);
}
#Override
protected void onDraw(Canvas canvas)
{
float yRatio = 1092/ _height;
float xRatio = 800 / _width;
Point lastPoint = null;
for (Point point : _Vectors)
{
switch (point.Type)
{
case Start:
{
lastPoint = point;
break;
}
case Midpoint:
case End:
{
canvas.drawLine(lastPoint.x / xRatio, lastPoint.y/ yRatio, point.x / xRatio, point.y/ yRatio, _paint);
lastPoint = point;
}
}
}
}
}
Referenced Classes:
public class Point
{
public float x, y;
public PointType Type;
}
public enum PointType
{
Start,
Midpoint ,
End;
}
XML for List row:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal" >
<TextView
android:id="#+id/Page_Num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:layout_marginRight="5dp"
android:text="1"
android:textAppearance="?android:attr/textAppearanceMedium" />
<LinearLayout
android:id="#+id/Page_Canvas"
android:layout_width="130dip"
android:layout_height="130dip"
android:gravity="center_vertical|center_horizontal"
android:layout_toRightOf="#id/Page_Num">
</LinearLayout>
<TextView
android:id="#+id/Page_Date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:layout_marginLeft="5dp"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_toRightOf="#id/Page_Canvas">
</TextView>
</RelativeLayout>