I'm pretty new to android programming. And now, I'm trying to create a ImageView programmatically. But not in the MainActivity, but in a second class I created.
Here's my code:
package com.example.joystick;
import android.app.Activity;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
public class Joystick extends Activity{
ImageView imView;
RelativeLayout Layout;
public void create(int x, int y, RelativeLayout layout) {
Layout = layout;
imView = new ImageView(this);
imView.setImageResource(R.drawable.joystickknuppel);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(x, y, 0, 0);
imView.setLayoutParams(params);
Layout.addView(imView);
}
}
If I load the app to my device, it crashes. But if I copy the "create" method into MainActivity, it works well.
Can someone tell me why this happens, and how to fix it?
Thanks a lot!
Replace this method with this code...onCreateView(...)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
Layout = layout;
imView = new ImageView(this);
imView.setImageResource(R.drawable.joystickknuppel);
params.setMargins(x, y, 0, 0);
imView.setLayoutParams(params);
Layout.addView(imView);
return view;
}
Don't forget to initialize the Layout Layout
Is Joystick an Activity?? If not, you don't need to extend Activity instead pass a reference of your current activity and then use it inside your create method.
Here's how.
public class Joystick {
ImageView imView;
RelativeLayout Layout;
Activity activity;
public JoyStick(Activity activity)
{
this.activity=activity;
}
public void create(int x, int y, RelativeLayout layout) {
Layout = layout;
//here's the change
imView = new ImageView(activity);
imView.setImageResource(R.drawable.joystickknuppel);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutPa rams.WRAP_CONTENT);
params.setMargins(x, y, 0, 0);
imView.setLayoutParams(params);
Layout.addView(imView);
}
}
From your MainActivity you can acces it via.
JoyStick joyStick=new JoyStick(this);
//add your params
joystick.create(x,y,relativeLayout);
Call this create method from onCreate and send layout as a parameter to this function. Actually you are trying to access the view that is not registered yet. So in this case you will get null pointer exception.
You must have activity referance to add control on that perticular activity
package com.example.joystick;
import android.app.Activity;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
public class Joystick{
ImageView imView;
RelativeLayout Layout;
public void create(Activity act,int x, int y, RelativeLayout layout) {
Layout = layout;
imView = new ImageView(act);
imView.setImageResource(R.drawable.joystickknuppel);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(x, y, 0, 0);
imView.setLayoutParams(params);
Layout.addView(imView);
}
}
if you put 'this' it means it point to your Joystick class which extends Activity but not actually Intent from anywhere.
from which Activity you call create mathod there you have to pass 'this' as Activity parameter.
Related
I've been following [this] answer to try to correct the heights of some image buttons in a scroll view, since I can't use linear layout weights. This is the code I've come up with:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_plant_list);
final ImageButton buttonPlant1= (ImageView) findViewById(R.id.buttonPlant1);
final ScrollView scrollViewPlant = (ScrollView) findViewById(R.id.scrollViewPlant);
scrollViewPlant.post(new Runnable() {
#Override
public void run() {
int scrollPlantHeight = scrollViewPlant.getHeight();
LinearLayout.LayoutParams layoutParams = new
LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, scrollPlantHeight / 3.69);
buttonPlant1.setLayoutParams(layoutParams);
}
});
}
I keep getting an alert in my code telling me that I need to import a class before the "LayoutParams.MATCH_PARENT."
When I import a class (I'm not even sure which one to import, but all I've tried is LinearLayout.LayoutParams), I get another alert telling me that it "Cannot resolve constructor 'LayoutParams(int, double)'" at the LayoutParams.MATCH_PARENT.
What class should I import? If any? Or is there something else I'm missing?
LayoutParams Class doesn't have constructor with (int, double).
Try casting the second parameter to integer.
LinearLayout.LayoutParams layoutParams = new
LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(scrollPlantHeight / 3.69));
I need to overlap an image view with textview. And this combined view will be repeated 100 times in a LinearLayout. I was thinking of using FrameLayout in LinearLayout and Repeating the FrameLayout in LinearLayout 100 times when FrameLayout holds the imageview and textview overlapped.
Need to do this programatically not from xml file.
I added the image and textview to framelayout first then tried to add the framelayout to linearlayout. But it says : the specified child has already a parent.. so not working. Can you please show me in code? Thanks for your help.
it is going to be like this, but need to be done programmaticaly
---linear layout--------------
------------------------------
|frame layout----------------|
||txt view on top of img view|
------------------------------
frame layout will be repeated|
---/end of linear layout------
Also here is the separated code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
LinearLayout dynamicview = (LinearLayout) findViewById(R.id.main_layout);
FrameLayout barFrameLayout = new FrameLayout(this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
barFrameLayout.setLayoutParams(params);
LinearLayout.LayoutParams slparams1 = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
for (int i = 65; i <= 75; i++) {
TextView catTV = new TextView(this);
catTV.setLayoutParams(slparams1);
catTV.setText("===" + Character.toString((char) i) + "===");
catTV.setTextSize(32);
ImageView iv = new ImageView(this);
iv.setImageResource(R.drawable.ic_launcher);
iv.setLayoutParams(slparams1);
barFrameLayout.addView(catTV);
barFrameLayout.addView(iv);
dynamicview.addView(barFrameLayout);
}
}
Here is the code to demonstrate what you are trying to achieve. I have used RelativeLayout, which is very flexible, you can position the elements easily relative to others.( if you need to change to FrameLayout you can change ).
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class ExampleLayout extends LinearLayout{
public ExampleLayout(Context context,AttributeSet attrs){
super(context,attrs);
for(int i =0; i< 100; i++){
RelativeLayout childLayout = new RelativeLayout(context);
ImageView img = new ImageView(context);
TextView text = new TextView(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
childLayout.addView(img, params);
params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
childLayout.addView(text, params);
LinearLayout.LayoutParams parentParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
this.addView(childLayout,parentParams);
}
}
}
You can then use the ExampleLayout class to add it to any of the layout.xml file.
FrameLayout is designed to block out an area on the screen to display
a single item
(source: http://developer.android.com/reference/android/widget/FrameLayout.html).
Anyway, you have to create new FrameLayouts not use the same.
When you're doing:
barFrameLayout.addView(catTV);
barFrameLayout.addView(iv);
dynamicview.addView(barFrameLayout);
you're always adding these new objects (catTV and iv) to the same instance of a FrameLayout (barFrameLayout).
I don't think that's what you wanted to do.
I am creating my own UI component class by extending layout class. Now instead of adding my custom attributes like height , width etc in string xml resources as separate xml, I want to define in Activity class itself. I don't want to add in xml and then refer it from R class in my code. Is there any way to define and access attributes in code itself?
Here is a code sample from Pro Android 4:
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity
{
private LinearLayout nameContainer;
private LinearLayout addressContainer;
private LinearLayout parentContainer;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
createNameContainer();
createAddressContainer();
createParentContainer();
setContentView(parentContainer);
}
private void createNameContainer()
{
nameContainer = new LinearLayout(this);
nameContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT));
nameContainer.setOrientation(LinearLayout.HORIZONTAL);
TextView nameLbl = new TextView(this);
nameLbl.setText("Name: ");
TextView nameValue = new TextView(this);
nameValue.setText("John Doe");
nameContainer.addView(nameLbl);
nameContainer.addView(nameValue);
}
private void createAddressContainer()
{
addressContainer = new LinearLayout(this);
addressContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT));
addressContainer.setOrientation(LinearLayout.VERTICAL);
TextView addrLbl = new TextView(this);
addrLbl.setText("Address:");
TextView addrValue = new TextView(this);
addrValue.setText("911 Hollywood Blvd");
addressContainer.addView(addrLbl);
addressContainer.addView(addrValue);
}
private void createParentContainer()
{
parentContainer = new LinearLayout(this);
parentContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
parentContainer.setOrientation(LinearLayout.VERTICAL);
parentContainer.addView(nameContainer);
parentContainer.addView(addressContainer);
}
}
Also See these for more functionalities:
Layout Params Linear Layout
ViewGroup Layout Params
StackOverflow Setting Layout params
When you create a LiveWallpaper in Android 2.2+ you get a canvas (or whatever the 3D equivalent is) to draw on. I'd like to draw some elements using the built-in Android UI tools rather than building everything from scratch using canvas commands or a loading a pre-rendered UI bitmap.
Converting a single View to a Bitmap works fine. i.e. this works great:
// For example this works:
TextView view = new TextView(ctx);
view.layout(0, 0, 200, 100);
view.setText("test");
Bitmap b = Bitmap.createBitmap( 200, 100, Bitmap.Config.ARGB_8888);
Canvas tempC = new Canvas(b);
view.draw(tempC);
c.drawBitmap(b, 200, 100, mPaint);
But, converting a LinearLayout with children causes problems. You only get the LinearLayout itself and none of it's children. For example, if I set the LinearLayout to have a white background I get a nicely rendered white box, but none of the TextView children are in the Bitmap. I've also tried using DrawingCache with similar results.
The code I'm using is the cube example with the only changes being an extra draw command. The LinearLayout works fine as a toast or as a regular view (i.e. everything nicely shows up), on the LiveWallpaper all I get is the LinearLayout's background rendered.
inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (LinearLayout) inflater.inflate(com.example.android.livecubes.R.layout.testLinearLayout, null);
layout.layout(0, 0, 400, 200);
Bitmap b = Bitmap.createBitmap( 400, 200, Bitmap.Config.ARGB_8888);
Canvas tempC = new Canvas(b);
layout.draw(tempC);
c.drawBitmap(b, 10, 200, mPaint);
Does anyone know if you need to do anything special to get the children rendered properly to my bitmap? i.e. do I need to somehow do something special to make the layout render the rest of the children? Should I write a function to recursively do something to all the children?
I could composite everything myself but, since the display is fairly static (i.e. I draw this once and keep a copy of the bitmap to draw on the background) this seems easier on me and still pretty efficient.
Edit:
While digging a bit more into the state of the Layout it looks as though the layout is not progressing down the view tree (i.e. the LinearLayout gets its layout computed when I call layout() but the children have a null (0x0) size). According to the Romain Guy's post in 2008 android developer post. You have to wait for the layout pass or force the layout yourself. The problem is how can I wait for a layout pass from a wall paper engine for a LinearLayout that is not attached to the root view group? And how can I manually layout each child element if layout requires you to set the left, top, right, bottom when I don't know what these should be.
I've tried calling forceLayout on the children but it doesn't seem to help either. I'm not sure how the layout framework works behind the scenes (besides that it does a two pass layout). Is there a way to manually make it do the layout pass, i.e. right now? Since it's not an Activity I don't think a lot of the normal background stuff is happening quite the way I'd like.
Live Wallpapers were very specifically designed to NOT use standard UI widgets. However it is possible to use them anyway. You will have to force a layout pass yourself by first calling measure() on the View, then layout(). You can find more information in my presentation.
Here's an example of a view group, button and imageview laid out and displayed in a Live Wallpapers. You can also work around the null window token bug and add views directly via WindowManager if you set the the Window type to 0. You have to catch the exception it throws and the results are somewhat erratic but it works for the most part.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class UIWidgetWallpaper extends WallpaperService
{
private final String TAG = getClass().getSimpleName();
final static int pixFormat = PixelFormat.RGBA_8888;
protected ImageView imageView;
protected WindowManager windowManager;
protected LayoutParams layoutParams;
protected WidgetGroup widgetGroup;
protected SurfaceHolder surfaceHolder;
protected Button button;
#Override
public Engine onCreateEngine()
{
Log.i( TAG, "onCreateEngine" );
return new UIWidgetWallpaperEngine();
}
public class WidgetGroup extends ViewGroup
{
private final String TAG = getClass().getSimpleName();
public WidgetGroup( Context context )
{
super( context );
Log.i( TAG, "WidgetGroup" );
setWillNotDraw( true );
}
#Override
protected void onLayout( boolean changed, int l, int t, int r, int b )
{
layout( l, t, r, b );
}
}
public class UIWidgetWallpaperEngine extends Engine implements Callback
{
private final String TAG = getClass().getSimpleName();
#Override
public void onCreate( SurfaceHolder holder )
{
Log.i( TAG, "onCreate" );
super.onCreate( holder );
surfaceHolder = holder;
surfaceHolder.addCallback( this );
imageView = new ImageView( getApplicationContext() );
imageView.setClickable( false );
imageView.setImageResource( R.drawable.icon );
widgetGroup = new WidgetGroup( getApplicationContext() );
widgetGroup.setBackgroundDrawable( getWallpaper() );
widgetGroup.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) );
widgetGroup.setAddStatesFromChildren( true );
holder.setFormat( pixFormat );
LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT );
imageParams.weight = 1.0f;
imageParams.gravity = Gravity.CENTER;
widgetGroup.addView( imageView, imageParams );
button = new Button( getApplicationContext() );
button.setText( "Test Button" );
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT );
buttonParams.weight = 1.0f;
buttonParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
widgetGroup.addView( button, buttonParams );
}
#Override
public void surfaceChanged( SurfaceHolder holder, int format, int width, int height )
{
Log.i( TAG, "surfaceChanged: " );
synchronized( surfaceHolder )
{
Canvas canvas = surfaceHolder.lockCanvas();
widgetGroup.layout( 0, 0, width, height );
imageView.layout( 0, 0, width / 2, height );
button.layout( width / 2, height - 100, width, height );
widgetGroup.draw( canvas );
surfaceHolder.unlockCanvasAndPost( canvas );
}
}
#Override
public void surfaceCreated( SurfaceHolder holder )
{
}
#Override
public void surfaceDestroyed( SurfaceHolder holder )
{
}
}
}
I'm trying to add views dynamically to a linearlayout.
I see through getChildCount() that the views are added to the layout, but even calling invalidate() on the layout doesn't give me the childs showed up.
Am I missing something?
A couple of things you can check in your code:
On the View that is being added, check that you call its setLayoutParameter method with an appropriate ViewGroup.LayoutParameter.
When you adding the new Views, make sure you are doing it on the UI thread. To do this, you can use the parent View's post method.
This self contained example adds a TextView after a short delay when it starts:
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ProgrammticView extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
setContentView(layout);
// This is just going to programatically add a view after a short delay.
Timer timing = new Timer();
timing.schedule(new TimerTask() {
#Override
public void run() {
final TextView child = new TextView(ProgrammticView.this);
child.setText("Hello World!");
child.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
// When adding another view, make sure you do it on the UI
// thread.
layout.post(new Runnable() {
public void run() {
layout.addView(child);
}
});
}
}, 5000);
}
}
I had the same problem and noticed that my overriden onMeasure() method wasn't called after the invalidate. So I created my own subroutine in the LinearLayout and called it before the invalidate() method.
Here is the code for an vertical LinearLayout:
private void measure() {
if (this.getOrientation() == LinearLayout.VERTICAL) {
int h = 0;
int w = 0;
this.measureChildren(0, 0);
for (int i = 0; i < this.getChildCount(); i++) {
View v = this.getChildAt(i);
h += v.getMeasuredHeight();
w = (w < v.getMeasuredWidth()) ? v.getMeasuredWidth() : w;
}
height = (h < height) ? height : h;
width = (w < width) ? width : w;
}
this.setMeasuredDimension(width, height);
}
I spent a lot of time to solve this problem too. And I found a simple method of refresh LinearLayout in 3 lines of code
You must set transperent color in style.xml
<color name="transparent">#00000000</color>
And in the code just call to set background
LinearLayout ll = (LinearLayout) findViewById(R.id.noteList);
ll.setBackgroundColor(getResources().getColor(R.color.transparent));
ll.invalidate();
If you has drawable background call
ll.setBackgroundResource(R.drawable.your_drawable);