Returning values from custom view - android

I have made a class ShowWeight extending LinearLayout, which has two custom Views as Inner classes. I am using this class by means of an XML tag in the main.xml :
<org.test.ShowWeight android:id="#+id/sw"..................../>
There are two public variables in the ShowWeight class, whose changing values need to be captured in the main activity, which uses main.xml as its view.
How do I do this?
I tried this in the main activity :
ShowWeight sw=(ShowWeight)this.findViewById(R.id.sw);
Log.d("test",sw.getContentDescription().toString());
and this in the showWeight class:
this.setContentDescription(/*required value */);
This resulted in a NullPointerException.
Would appreciate some suggestions (Database, static variables, not an option)
Update:
Unfortunately, I am not permitted to post any of the code, I apologize if I seem vague, nevertheless I'm sure the ShowWeight class hasn't altered anything that might be causing the problem.
The ShowWeight class, which I have added to the main.xml view by means of an XML tag appears fine and functions properly.
When I use ShowWeight sw=(ShowWeight)this.findViewById(R.id.sw); in the Main Activity and then Toast or print ShowWeight I am getting 'null' . Also the setContentDescription(),getContentDescription() shouldn't throw errors because I've given a default contentDescription in the XML tag for ShowWeight.

Posting your ShowWeight class will help us more.
Assuming that you have class like this.
public class ShowWeight extends LinearLayout {
private Object myObject;
public ShowWeight(Context context, AttributeSet attrs)
{
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.header, this);
}
public Object getMyObject()
{
return myObject;
}
}
and in you MainActivity.java
ShowWeight sw=(ShowWeight)this.findViewById(R.id.sw);
sw.getMyObject();

Related

Creating sharable Resource Drawables without static must be possible what am I missing?

I'm pretty far along in a project and have hit an undesirable snag and unable to find a solution. I have some sudo code that I believe will illustrate what I am trying to do but is not full code. The error is NullPointerException on the ContextWrapper in the Main class when attempting to pass it to an ImageHandler. Unfortunately, ImageViews and I suspect all Views, try to resolve their drawables before an activity has called the onCreate. So I had to try and store images into the Activity Constructor but then I am unable to pass the context. I have considered AsyncTask or the handler to wait for the onCreate() of the activity but then I might as well go back to SurfaceView and creating my own designs. I will continue to look but if someone with more android experience then me knows of either better design or the solution the help would be tremendous. Also the eclipse layout previewer being compatible is highly preferred. I have also tried to have each imageview independently have the desired layerdrawables even though alot of them are the same but the performance is terrible. Had to add the constructor in the Activity because if the imagehandler is created in the onCreate it will be null when the the ImageView tries to call it on it's constructor. Why the Activities components ready themselves before Activity.onCreate is called is something I don't quite understand yet.
public class MainActivity extends Activity {
ImageHandler ih;
MainActivity(){
Log.d("Acitivity", "Constructed");
ih=new ImageHandler(this);
//ih=new ImageHandler(this.getApplicationContext());
//ih=new ImageHandler(this.getBaseContext());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("Activity", "Created");
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public LayerDrawable getImage(int some_identifier){
return ih.getImage(some_identifier);
}
}
===================
public class ImageHandler {
LayerDrawable[] image;
ImageHandler(Context context){
Log.d("ImageView", "Constructed");
//manipulate resource images to create custom layerdrawables and store.
//originally attempted to create a layer.xml and although it worked
//in the emulator perfectly the behavior was quite different in live tests.
//although it made sense any change to layer.xml images was global for all who used it
//the strange behavior was that the emulator behaved as desired.
}
public LayerDrawable getImage(int identifier){
return image[identifier];
}
}
====================
public class SomeImageView extends ImageView{
public SomeImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int some_identifier=0;
this.setImageDrawable(((MainActivity)context).getImage(some_identifier));
}
}
Thanks again community for any help.

Override Java constructor in Scala

In the docs, it says I should make the new class like this:
class MyView extends GLSurfaceView {
public MyView(Context context) {
super(context);
setRenderer(renderer);
}
}
Now I tried to re-do that in Scala:
class BaseGameActivity extends Activity {
object glview extends GLSurfaceView(this) {
setRenderer(renderer)
setEGLContextClientVersion(2)
}
}
However, the App crashes now with the exception "java.lang.IllegalStateException: setRenderer already called for this instance". I suspect this has to do with the way Scala calls the super-constructor.
I've tried to find out how to override the constructor in the way the docs describe, but couldn't find it. I'd appreciate any hint.
It seems to me that your are propagating the call to a different constructor from the base class. You are passing a reference to this instead of a reference to the Context object. It might be that this other constructor is calling setRenderer.
Could you try to create an inner class MyGLView like this:
class MyGLView(ctx: Context) extends GLSurfaceView(ctx) {
setRenderer(renderer)
}
And see what happens?
The problem is that object does not allow arguments to its constructor. Top-level objects must be initializable without any arguments (nobody calls their ctors). In your case you have an inner object, which can reference the members of the surrounding class instance. If you really need an inner object in your Activity class, you could do:
object glview extends GLSurfaceView(ctx) {
setRenderer(renderer)
}
where ctx is a member of the surrounding class.
In java likewise in scala constructors are not inherited.
So you can not override thing, you didnt inherit. And you should use one of existing constructors for base class. If all of them are calling setRenderer(renderer) it will be called during constructing super object and you obviously should not call it second time in a subtype constructor ( wheither it class, object or mixing-in trait ).

MonoDroid: Error when calling constructor of custom view - TwoDScrollView

I am building an Android application that uses the custom-built TwoDScrollView found here:
http://blog.gorges.us/2010/06/android-two-dimensional-scrollview/
This same class can be found referenced at several other websites, and others on Stack Overflow have asked questions with regard to it. I was using it in a previous Android application that I was building using Java/Eclipse, and I was having success.
With my current application, I wanted to use C# and MonoDroid. I decided to rewrite the entire TwoDScrollView class in C#. After rewriting it, and then using it in some layout XML, I get the following exceptions when trying to run my code:
System.NotSupportedException has been thrown. Unable to activate
instance of type MyProject.TwoDScrollView from native handle 44f4d310.
System.Exception: No constructor found for
MyProject.TwoDScrollView::.ctor(System.IntPtr,
Android.Runtime.JniHandleOwnership) ......with more text that
follows....
My layout XML is as follows:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<myproject.TwoDScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</myproject.TwoDScrollView>
</RelativeLayout>
Per the instructions at the following link on using custom views in layout XML in MonoDroid: http://docs.xamarin.com/android/advanced_topics/using_custom_views_in_a_layout
The constructors to the TwoDScrollView class look as follows:
public TwoDScrollView(Context context)
: base(context)
{
initTwoDScrollView();
}
public TwoDScrollView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
initTwoDScrollView();
}
public TwoDScrollView(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle)
{
initTwoDScrollView();
}
The same constructors exist in the C# version as in the Java version (which you can find at the above link). Any idea on what could be going wrong? I can post the full C# code of my TwoDScrollView if anyone would like to see it. It's essentially the same as the Java code bit for bit - except rewritten in C#.
Thanks for any help!
Congratulations! You've hit a leaky abstraction. :-/
The problem is this: for better or worse, virtual method calls from constructors invoke the most derived method implementation. C# is the same as Java in this respect; consider the following program:
using System;
class Base {
public Base ()
{
Console.WriteLine ("Base..ctor");
M ();
}
public virtual void M ()
{
Console.WriteLine ("Base.M");
}
}
class Derived : Base {
public Derived ()
{
Console.WriteLine ("Derived..ctor");
}
public override void M ()
{
Console.WriteLine ("Derived.M");
}
}
static class Demo {
public static void Main ()
{
new Derived ();
}
}
When run, the output is:
Base..ctor
Derived.M
Derived..ctor
That is, the Derived.M() method is invoked before the Derived constructor has executed.
In Mono for Android, things get more...complicated. The Android Callable Wrapper (ACW)'s constructor is invoked by Java and is responsible for creating the peer C# instance and mapping the Java instance to the C# instance. However, if a virtual method is invoked from the Java constructor, then the method will be dispatched before there is a C# instance to invoke the method upon!
Let that sink in a bit.
I don't know which method is triggering the scenario for your specific code (the code fragment you provided works fine), but we do have a sample which hits this scenario: LogTextBox overrides the TextView.DefaultMovementMethod property, and the TextView constructor invokes the getDefaultMovementMethod() method. The result is that Android tries to invoke LogTextBox.DefaultMovementMethod before a LogTextBox instance even exists.
So what does Mono for Android do? Mono for Android created the ACW, and thus knows which C# type the getDefaultMovementMethod() method should be delegated to. What it doesn't have is an instance, because one hasn't been created. So Mono for Android creates an instance of the appropriate type...via the (IntPtr, JniHandleOwnership) constructor, and generates an error if this constructor cannot be found.
Once the (in this case) TextView constructor finishes executing, the LogTextBox's ACW constructor will execute, at which point Mono for Android will go "aha! we've already created a C# instance for this Java instance", and will then invoke the appropriate constructor on the already created instance. Meaning that for a single instance, two constructors will be executed: the (IntPtr, JniHandleOwnership) constructor, and (later) the (Context, IAttributeSet, int) constructor.
The error message says:
System.Exception: No constructor found for MyProject.TwoDScrollView::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
Try adding a constructor like it says and see if that helps:
public TwoDScrollView (IntPtr a, JniHandleOwnership b) : base (a, b)
{
}
I had the same problem with a custom imageview and the answer for jpobst certainly fixed the problem completely :
public CircularImageView(Context context)
:base(context)
{
init (context, null, 0);
}
public CircularImageView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
init (context, attrs, Resource.Attribute.circularImageViewStyle);
}
public CircularImageView(Context context, IAttributeSet attrs, int defStyle)
:base(context, attrs, defStyle)
{
init(context, attrs, defStyle);
}
public CircularImageView (IntPtr a, JniHandleOwnership b) : base (a, b)
{
}
I was using custom list view renderer, but none of the work arounds worked for me. But delaying the base.Dispose method helped me fix the crash, probably this gives the mono android, the chance to initialize the proxy instance.
Xamarin.Forms.Device.BeginInvokeOnMainThread(base.Dispose);
I don't see any crashes now!

How to avoid static context reference when I need to use a activity context?

After read this topic avoiding memory leaks some doubts arouse.
If I need to use an activity context (example: inflate a view in a PopupWindow class to show a popup) how can I hold the context of actual activity to do it? If I need to avoid a static context reference the only way to do it is creating an attribute in my class? And all the other classes I'll need the actual activity context I need to do it?
update-
I want to use this actual activity context in many classes that don't inherited Context, like I use with the application Context in my Application class that has a static method called getApplicationContext() declared. This method follows the Singleton Design Pattern and works fine.
Working from the code you linked in the comments, why not do this:
//my main activity
public class ExampleStaticReferenceActivity extends Activity {
//...
public void methodCalledWhenUserPressesButton(){
LinearLayout masterLayout = (LinearLayout) findViewById(R.id.masterLayout);
//now passing a reference to the current activity - elevine
masterLayout.addView(ButtonCreator.createButton(this));
}
}
//this class is in another package
public class ButtonCreator {
//added a Context parameter - elevine
public static Button createButton(Context context) {
Button button;
button = new Button(context);
//... some configurations for button
return button;
}
}
That will crash your Application since Your Activity will be killed by OS when it runs out of Resources thus Context will also be null.. And its meaningless to give A background Activities Instance when you want to show pop up in the Foreground Activity.. What the Blog says is avoid passing activity.this where even getApplicationContext() can do the job..

call getString(R.strings....) from class?

Is there a way to use the getString method from a seperate class?
I have a string stored in my strings xml, I'd like to use that string in an object... but the method isn't even available in the object...
any tips?
getString() is a method of the Context class¹. If you need it inside a seperate class (that does not extend Context), it's usually best to provide it as a seperate argument to the method that needs it.
Example:
public void logString(Context c, int stringId) {
Log.d("TAG", c.getString(stringId));
}
One thing is important: Never store the context inside the separate class.
Provide an argument. Otherwise you will leak memory and disrupt the whole android lifecycle if the object that stores the context lives longer than the object where the context originally belongs to (e.g. an activity).
¹ getString() can also be used from the Resources class - which you can get via Context.getResources()
the solution here is to make sure your object has a reference to the application context
Class Swag{
private Context ctx;
public Swag(Context ctx){
this.ctx = ctx;
}
public void doSomething(){
String something = ctx.getResources().getString(R.string.somestring);
...
}
// or like this
public void makeUpperCase(Context appContext){
appContext.getResources().getString(R.string.super_string_swag_yolo);
}
}
obviously you'd have to supply the context when creating an object or when caling the method
resouce file: values/strings.xml
<resources>
<string name="app_name">App name</string>
<resources>
java
import android.content.res.Resources;
Resources.getSystem().getString(R.string.app_name);//result : App name
edit:
The below will NOT work. I read this on another site and assumed it worked, but I just tried it in my app and kept getting an error. Problem is, it will compile but you will get a runtime exception.
This will work from any java class:
import android.content.res.Resources
Resources.getSystem().getString(R.string.blah);
if you cannot pass a context as parameter, create another class, where you put all your static data.
example :
public class StaticData {
public static String BASE_URL = "https://stackoverflowrocks.com";
}
and get that string from your other class by calling directly
StaticData.BASE_URL
nice and clean.
This works, but for SYSTEM resources only:
import android.content.res.Resources
Resources.getSystem().getString(R.string.blah);
Reference: https://stackoverflow.com/a/40917607/8994882
Try this in your java file:
String myString = getResources().getString(R.string.MY_STRING)
Now use this string object.

Categories

Resources