Drawing on a screen with other views in android - android

I have been searching for the past few hours to the answer to a very dumb question. I know how to draw on the canvas in android if you extend the view class, modify onDraw and set setContentView() to a new instance of that class. However, I need to also have 2 TextViews and 2 EditTexts at the bottom of the activity and if setContentView() is set to only have that view, these views will, obviously, not display. How can put all of these on the screen?
EDIT: Here is my code: (the package name is android.physicsengine)
package android.physicsengine;
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.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout;
public class ResultantForceEngine extends Activity {
private EditText mag;
private EditText dir;
private View image;
private RelativeLayout layout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.resultant_force);
mag = (EditText)findViewById(R.id.magnitude);
dir = (EditText)findViewById(R.id.direction);
}
public class MyView extends View{
public MyView(Context context){
super(context);
}
public MyView(Context context, AttributeSet attrs){
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(Color.RED);
canvas.drawLine(canvas.getWidth()/2, canvas.getHeight()/2-200, canvas.getWidth()/2 ,canvas.getHeight()/2+200, circlePaint);
canvas.drawLine(canvas.getWidth()/2-200, canvas.getHeight()/2, canvas.getWidth()/2+200 ,canvas.getHeight()/2, circlePaint);
}
}
}
and the xml
<view class="android.physicsengine.ResultantForceEngine$MyView"
android:id="#+id/image"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<TextView android:id="#+id/magText"
android:text="Magnitude (N) ="
android:textSize="15dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="3dip"
android:gravity="center_vertical"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" />
<EditText android:id="#+id/magnitude"
android:inputType="numberDecimal"
android:layout_alignParentBottom="true"
android:layout_toRightOf ="#id/magText"
android:layout_width="wrap_content"
android:padding="3dip"
android:layout_height="wrap_content" />
<TextView android:id="#+id/dirText"
android:text="Angle (deg) ="
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dip"
android:layout_alignParentBottom="true"
android:layout_toRightOf ="#id/magnitude"
android:padding="3dip"
android:gravity="center_vertical" />
<EditText android:id="#+id/direction"
android:inputType="numberDecimal"
android:layout_alignParentBottom="true"
android:layout_toRightOf ="#id/dirText"
android:layout_alignParentRight="true"
android:layout_height="wrap_content"
android:padding="3dip"
android:layout_width="wrap_content"/>

Basically you need to define your XML file with your custom view class and the other widgets you have. In your case, there would be the custom view, 2 textviews and 2 edittexts in the XML file.
You define the custom view in XML just like any other widget, except you use the namespace of the view and the class name.
<com.example.android.myCustomView
android:id="#+id/my_custom_view"
...
Then inside your activity a simple call
setContentView(R.layout.main);
Edit: The problem is your class is private, so it "isn't seen" when your activity makes a call the layout and tries to inflate it.
Edit2: Of course this won't work, you're using an inner class! You have to communicate stuff like this if you expect to get answers.
The XML syntax for an inner class is different.
<view class="com.example.android.MyClass$MyInnerClass"

You should be able to use the class that you extended in your res/layout/main.xml just like you would any other View class, and add your TextView and EditTexts in the layout .xml file like normal. I have not personally done this, but I have used custom View classes in this way.
Then for your setContentView you would just use the layout .xml file like:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...

Related

Android Casting Custom View

This is my view's XML file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/bsv_edit_beaconname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="textPersonName"
android:text="Beacon Name" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/bsv_check_fix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/cinit_table_isfixpoint" />
<CheckBox
android:id="#+id/bsv_check_draw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/cinit_table_drawbeacon" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="#+id/bsv_btn_setpos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="set_position_click"
android:text="#string/cinit_set_position" />
<TextView
android:id="#+id/bsv_text_pos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Position ?" />
</LinearLayout>
With the corresponding java class being the following
package talogs.beacontalogs;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
public class BeaconSetupView extends LinearLayout {
private static final String TAG = "BeaconSetupView";
private EditText edit_beaconName;
private CheckBox check_isFixpoint;
private CheckBox check_draw;
private Button btn_setpos;
private TextView text_pos;
public BeaconSetupView(Context context) {
this(context, null);
}
public BeaconSetupView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BeaconSetupView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public void init(Context context) {
// inflate(getContext(), R.layout.beacon_setup_view, this);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate( R.layout.beacon_setup_view, this );
edit_beaconName = (EditText) findViewById(R.id.bsv_edit_beaconname);
check_isFixpoint = (CheckBox) findViewById(R.id.bsv_check_fix);
check_draw = (CheckBox) findViewById(R.id.bsv_check_draw);
btn_setpos = (Button) findViewById(R.id.bsv_btn_setpos);
text_pos = (TextView) findViewById(R.id.bsv_text_pos);
}
public void setPosition(Point point) {
Log.v(TAG, "setting position to " + point.toString());
text_pos.setText(String.format("(%d, %d)", point.x, point.y));
}
}
My custom view BeaconSetupView is created programmatically using this code when a button in an activity is pressed.
public void btn_addbeacon_Click(View view) {
BeaconSetupView beacon = new BeaconSetupView(this);
beaconList.addView(beacon, beaconList.getChildCount() - 1);
}
The button that's part of the custom view calls this method:
public void set_position_click(View view) {
ViewParent parent1 = view.getParent();
ViewParent parent2 = parent1.getParent();
BeaconSetupView bsv = (BeaconSetupView) view.getParent().getParent();
Log.v(LOG_TAG, "set_position called, point is currently " + String.valueOf(lastClicked));
if (lastClicked != null) {
bsv.setPosition(lastClicked);
}
}
The problem is that the button click cannot be implemented outside of the root activity (because it has data that I need), but I also need to get its corresponding BeaconSetupView. Calling view.getParent().getParent() and casting it results in a ClassCastException. So I tried changing the root element of the XML layout to BeaconSetupView, but that causes the app to crash when the view is first added to the activity.
I either need a way to cast my custom class properly and still be able to add it programmatically to my activity, or I need to be able to access the activity's instance from another class that doesn't have a reference to it. What are my options?

Draw View from XML file under other TextView

I'm trying to draw a Canvas in a View and show this View under some TextViews I defined in my XML file.
Everytime I test the App it just doesn't start on the device.
However, the parts themself work:
When I change the setContentView(R.layout.activity_main); in the onCreate() to setContetView(new CustomView(this)); it works, but of course without the textView.
My activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="#+id/sampleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"/>
<com.example.simon.drawtest.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/sampleText"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
My MainActivity.java:
package com.example.simon.drawtest;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
My CustomView.java:
package com.example.simon.drawtest;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class CustomView extends View {
private Paint paint;
public CustomView(Context context) {
super(context);
paint = new Paint();
paint.setColor(Color.GRAY);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);
canvas.drawCircle(200, 200, 100, paint);
}
}
show your whole activity_main.xml...
both Views should be wrapped in e.g. FrameLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/sampleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"/>
<com.example.simon.drawtest.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/sampleText"
android:layout_alignParentBottom="true"/>
</FrameLayout>
edit: ok, so you have RelativeLayout.. also note that with Android 5.0 elevation attribute was introduced and you may have some problems with drawing order like HERE
also... your TextView is first in XML so it will be drawn before your custom view (drawing by order if elevation isn't set)
also... android:layout_below="#+id/sampleText" below means under in vertical axis. try to remove this line
edit: user post exception stack trace in comments, so below I'm pasting proper answer for his case (comment copied):
your error stack says
Caused by: java.lang.NoSuchMethodException:
[class android.content.Context, interface
android.util.AttributeSet]
you have only one constructor with Context
only, system needs also one with additional AttributeSet. check out
THIS question & answer, apply proper constructors for your CustomView

View Custom Layout in grapical layout edit screen

This is my source of testing Custom Layout (shows image and label).
XML CODE
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/screen_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#3535ff"
android:orientation="vertical"
android:padding="1dp" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000000"
android:padding="20dp" >
<ImageView
android:id="#+id/screen_image"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:background="#000000"
android:scaleType="centerInside"
android:src="#drawable/cam_1_20130117_105601_118" />
<TextView
android:id="#+id/screen_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="#id/screen_image"
android:layout_centerHorizontal="true"
android:layout_marginBottom="3dp"
android:background="#95000000"
android:gravity="center_vertical|center_horizontal"
android:text="사출성형기 1호기"
android:textColor="#ffffff" />
</RelativeLayout>
</LinearLayout>
JAVA CODE
package com.example.testlayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.testscreen.R;
public class CustomScreen extends LinearLayout {
LinearLayout mLayout = null;
TextView mLabel = null;
ImageView mImage = null;
public CustomScreen(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomScreen(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomScreen(Context context) {
super(context);
init();
}
void init() {
LayoutInflater inflater = (LayoutInflater)getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.screen, this, true);
// mLabel = (TextView)findViewById(R.id.screen_label);
// mImage = (ImageView)findViewById(R.id.screen_image);
if (isInEditMode()) {
return;
}
}
}
With this code, I checked on Nexus One. It displays well.
The problem is on editor mode. Exactly on xml editer with preview.
When I add this view, error message appeared.
The following classes could not be instantiated :
- com.example.testlayout.CustomScreen (Open Class, Show Error Log) See the Error Log (Window > Show View) for more details. Tip: Use
View.isInEditMode() in your custom views to skip code when shown in
Eclipse
I want to check it on edit mode.
How can I check well like other views?
you have to add (!isInEditMode()) in each constructor. which tells if it not in edit Mode then initialized the stuff
if(!isInEditMode())
init(context);
and for initializing stuff follow this
also have a look at this

Simple custom control - Force closed

I have created an android project using eclipse.the default system generated code looked as below
package com.rmycustomclass.bengler;
import android.app.Activity;
import android.os.Bundle;
public class RMyCustomClassActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
I wanted to try a simple custom control , so i changed the code as below. when i searched the web to create a simple custom control there are mentioned like "create class by subclassing a View". so tried as below by modifying the code.but it was not running and always force closed.
package com.rmycustomclass.bengler;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class RMyCustomClassActivity extends View {
private Paint myPaint;
public RMyCustomClassActivity(Context cxt, AttributeSet attribute){
super(cxt,attribute);
init();
}
public RMyCustomClassActivity(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
public void init(){
myPaint = new Paint();
myPaint.setTextSize(12);
myPaint.setColor(0xFF668800);
myPaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawText("TEEEST", 100, 100, myPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
this.setMeasuredDimension(150,200);
}
}
Below is my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:orientation="vertical">
<TextView
android:id="#+id/textView1"
android:text="Enter text"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tableLayout1">
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:layout_weight="1">
</Button>
<Button android:layout_width="wrap_content" android:id="#+id/button1" android:layout_height="40dip" android:text="My button" android:layout_weight="1"></Button>
<test.control
android:id="#+id/control"
android:layout_height="match_parent"> </test.control>
</TableLayout>
</LinearLayout>
When you create a project in eclipse it links your activity (RMyCustomClassActivity in your case) as the launcher in Android manifest...But since you have changed the activity to a View
android runtime cannot find the activity to launch...See your manifest file and you will find your class as the launcher activity
add android:layout_width="match_parent"
<test.control
android:id="#+id/control"
android:layout_height="match_parent"
android:layout_width="match_parent"
>

How to pass AttributeSet to custom View

How do I pass the current AttributeSet to a custom View class? If I use a constructor that only has Context in the arguments, I lose all themes and the ability to use "style" tags in the xml for that custom View.
What I've done is create an activity that contains my custom view already in the xml file, and then programmatically create a new one and add it to the layout. What I find is the one that is made in the xml has the proper styling, and the one I create programmatically doesn't.
The difference between the two as far as I can tell is that the system uses the CustomLayout1(Context context, AttributeSet attrs) constructor. The problem is I can't figure out how to get the AttributeSet for the application to pass to this custom view when I create it programmatically.
Here's the Activity:
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
public class ThemeOne extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.mainlayout);
layout.addView(new CustomLayout1(getApplicationContext()));
}
}
Here's the main xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="#+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.clearsync.test.theme1.CustomLayout1 android:id="#+id/maincustom"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
The custom view class:
import com.clearsync.test.theme1.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class CustomLayout1 extends LinearLayout {
private Context context = null;
public CustomLayout1(Context context) {
super(context);
this.context = context;
create();
}
public CustomLayout1(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
create();
}
private void create(){
LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.inflateme, this, true);
}
}
and finally, the custom view 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">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Oh, Hewroh..."
style="?textview_style1" />
</LinearLayout>
Instead of building it with layout.addView(new CustomLayout1(getApplicationContext())); inflate it with the LayoutInflater in your Activity.
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.yourcustomviewxml, layout);
Your code creates LinearLayout inside of linear layout for your custom view. Correct way of doing this is changing your custom view xml from:
<?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">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Oh, Hewroh..."
style="?textview_style1" />
</LinearLayout>
to
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Oh, Hewroh..."
style="?textview_style1"
/>
</merge>
What are you trying to accomplish here? Looks like you have an endless recursive loop here using your create method, as inflate() will call the constructor that takes attributes. Anyways to answer your question you get the attributes in the constructor with the attributes!
That is the constructor that is called when loading from XML, otherwise it calls one of the other constructors that you supply.
One other helpful thing, you can get a reference to the inflater much easier from the static View method. View.inflate :D

Categories

Resources