Android - LinearLayout not adding custom view - java

NOTICE: THIS QUESTION HAS BEEN RESOLVED DUE TO ME CATCHING MY OWN STUPIDITY
I want to add a custom class to a LinearLayout, but for some reason I keep getting a NullPointerException.
Here is the method that deals with the addition:
protected void onPostExecute(String results) {
System.out.println("ON POST EXECUTE : " + results);
try {
if(!results.equals(((MessageBlurb)container.getChildAt(0)).getMessage())){
try {
container.removeViewAt(30);
for (int i = 29; i > 0; i--) {
container.addView(container.getChildAt(i-1), i);
container.removeViewAt(i-1);
}
container.addView(new MessageBlurb(getApplicationContext(), results, Color.BLACK), 0);
} catch (NullPointerException e) {
// TODO: handle exception
}
}
}
catch (Exception e) {
MessageBlurb mb = new MessageBlurb(getApplicationContext(), results, Color.BLACK);
mb.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
System.out.println(mb);
container.addView(mb, 0);
}
}
where MessageBlurb extends ViewGroup, because I have a TextView inside the MessageBlurb.
The MessageBlurb class looks like this:
public MessageBlurb(Context context, String message, int color){
super(context);
myTV = new TextView(context);
this.addView(myTV);
myTV.setText(message);
System.out.println("THE BLURB IS CREATED");
this.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
System.out.println("YOU CLIKED THE BLURB");
}
});
}
I printed out the description of mb, and it gives me a memory location. As well as that, the logcat error points to this line:
container.addView(mb, 0);
The container itself is a LinearLayout defined in the activity_main.xml file. It is initialized through the line of code:
container = (LinearLayout)findViewById(R.id.container);
The id of the Layout in the xml file is also called container
Can someone see what I'm doing wrong?
Thanks!

I'm not 100% sure if this is where the problem is, but the following code looks like it's just asking for trouble:
container.removeViewAt(30);
for (int i = 29; i > 0; i--) {
container.addView(container.getChildAt(i-1), i);
container.removeViewAt(i-1);
}
container.addView(new MessageBlurb(getApplicationContext(), results, Color.BLACK), 0);
It looks like you are trying to remove the last view in the container and add a new MessageBlurb at the top. But you don't need to shift all the views down manually like that, simply adding a new view at position 0 will do all that for you. Try replacing it with this:
container.removeViewAt(30);
container.addView(new MessageBlurb(getApplicationContext(), results, Color.BLACK), 0);
Also, do you have a good reason for using getApplicationContext() when creating the message blurb? Your code looks like its part of an AsyncTask class nested inside an Activity class, so you can and should pass the activity itself as the context (i.e. new MessageBlurb(Activity.this, results, Color.BLACK) or something similar).
Hope that helps!

It turns out that I was being a complete idiot. I looked through my onCreate() and found that I had commented out the line:
setContentView(...)
Even so, I want to thank everyone who replied to this thread.
Sorry for all the trouble! :)

Related

Cannot getText Because of wrong Id

I have been trying to solve this issue for a while but seems not to work. I am getting a nullPointer Exception on this Line school.setClasses(classesName.getText().toString());in the code block below.
Which is the code that actually posts to the SQLite Database
public void postSchoolSetuptoSQLite() {
school.setSchoolName(nameOfSchool.getText().toString());
school.setSchoolLocation(schoolLocation.getText().toString());
school.setClasses(classesName.getText().toString());
academeaSQL.addSchool(school);
if (demeaSQL != null) {
StringBuilder sb = new StringBuilder();
for (School s : demeaSQL.getAllSchools()) {
sb.append(" SchoolName= " + s.getSchoolName() + " SchoolLocation= " + s.getSchoolLocation()
+ " ClassName= " + s.getClasses());
sb.append("\n");
}
Log.i("Database content", sb.toString());
Toast.makeText(getApplicationContext(), "Added Successfully", Toast.LENGTH_LONG).show();
} else {
Log.i("Database Err", "Database Error");
}
}
I am creating the field for the classesName dynamically by clicking an "Add New Class" Button through this onAddField Method
public void onAddField(View v) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.field, null);
rowView.setId(ViewIdGenerator.generateViewId());
// Add the new row before the add field button.
parentLinearLayout.addView(rowView, parentLinearLayout.getChildCount() - 1);
Log.i("ids", String.valueOf(rowView.getId()));
}
The field Id is dynamically generated through the ViewIdGenerator Class.
The Error is at this point classesName = rowView.findViewById(rowView.getId()); when getting the Ids from the dynamically created fields in this code block
public void findByIds() {
rowView = new View(this);
parentLinearLayout = findViewById(R.id.parent_linear_layout);
nameOfSchool = findViewById(R.id.nameOfSchool);
schoolLocation = findViewById(R.id.schoolLocation);
addSchoolAndMoveNext = findViewById(R.id.addSchoolAndMoveNext);
classesName = rowView.findViewById(rowView.getId());
Log.i("Classname", String.valueOf(classesName));
Log.i("SchoolLoca", String.valueOf(schoolLocation));
}
Here is where I am calling the findByIds();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_school_search_setup);
findByIds();
intitializeListeners();
initializeObjects();
}
For the Nullpointer exception. the error is pointing at these areas
school.setClasses(classesName.getText().toString());
and
classesName = rowView.findViewById(rowView.getId());
Please why am I getting the nullpointer exception and How can I resolve it. Thank you. I will really appreciate your responses.
Your problem is that if you are calling findByIds() inside of onCreate() then rowView.getId() will return null, because you are setting the id in the default id generator AFTER the button gets pressed. onCreate() gets called when the activity first starts up.
It also looks like you are trying to add a list of classes when you hit the button. There are built in ways to do this in Android that have a bit more overhead than what you are doing (in terms of time it takes to implement) but that are going to be much more reliable for scrolling, adding new classes, deleting classes, etc.
Look into the recyclerview in order to do this
Inside of the Display Cursor contents in a RecyclerView section here will show you how to add data from the database to the RecyclerView

Scroll Android ListView to top without scroll animation [duplicate]

I have a ListActivity that implements onListItemClick() and calls a doSomething() function of the class. The latter contains l.setSelection(position) where l is the ListView object.
Now there is a onClickListener() listening for a button click that perfoms some actions and that too calls doSomething().
In the first case, the selected item get positioned appropriately, but in the latter, nothing happens.
Any clues about this strange behaviour and how I might make it work?
maybe you need to use function:
ListView.setItemChecked(int position, boolean checked);
use requestFocusFromTouch() before calling setSelection() method
I know this is an old question but I just had a similar problem that I solved in this way:
mListView.clearFocus();
mListView.post(new Runnable() {
#Override
public void run() {
mListView.setSelection(index);
}
});
You might need to wrap setSelection() in a posted Runnable (reference).
setSelection() does not necessarily have visual impact. The selection bar only appears if you use the D-pad/trackball to navigate the list. If you tap on the screen to click something, the selection bar appears briefly and vanishes.
Hence, setSelection() will only have a visual impact if the activity is not in touch mode (i.e., the last thing the user did was use the D-pad/trackball).
I am not 100% certain this explains your phenomenon given the description you provided, but I figured it is worth a shot...
If you use an Adapter for your ListView add this code to your adapter:
public class MyAdapter extends
ArrayAdapter<MyClass> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflator = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflator.inflate(R.layout.my_adapter, null);
} else {
rowView = (View) convertView;
}
//...
// set selected item
LinearLayout ActiveItem = (LinearLayout) rowView;
if (position == selectedItem)
{
ActiveItem
.setBackgroundResource(R.drawable.background_dark_blue);
// for focus on it
int top = (ActiveItem == null) ? 0 : ActiveItem.getTop();
((ListView) parent).setSelectionFromTop(position, top);
}
else
{
ActiveItem
.setBackgroundResource(R.drawable.border02);
}
}
private int selectedItem;
public void setSelectedItem(int position) {
selectedItem = position;
}
}
In your Activity:
myAdapter.setSelectedItem(1);
For me calling
listView.notifyDataSetChanged();
listView.requestFocusFromTouch();
and then
listView.setSelection(position);
solved the issue.
if you do that in a runnable it works without calling requestFocusFromTouch(), but the old position of the ListView is showen for a sekound.
I have an very large Request with Webcontent. When I used the code in onCreateView the Listview wasnt even finished loading.
I put it in onPostExecute of my AsyncTask.
//Get last position in listview
if (listView != null && scrollPosition != 0) {
listView.clearFocus();
listView.requestFocusFromTouch();
listView.post(new Runnable() {
#Override
public void run() {
listView.setItemChecked(scrollPosition, true);
listView.setSelection(scrollPosition);
}
});
}
Dont forget to set the item checked in on Click ;)
Maybe you should use the smoothScrollToPosition(int position) method of ListView
You can try 2 ways like these:
Solution A:
mListView.post(new Runnable() {
#Override
public void run() {
if (null != mListView) {
mListView.clearFocus();
mListView.requestFocusFromTouch();
mListView.setSelection(0);
}
}
});
In some complicated situation, this solution will bring some new problems in Android 8.x. Besides it may cause unexpected onFocusChange().
Solution B:
Define a custom view extends ListView. Override method handleDataChanged().Then setSelection(0). In CustomListView:
#Override
protected void handleDataChanged() {
super.handleDataChanged();
if (null != mHandleDataChangedListener){
mHandleDataChangedListener.onChanged();
}
}
HandleDataChangedListener mHandleDataChangedListener;
public void setHandleDataChangedListener(HandleDataChangedListener handleDataChangedListener) {
this.mHandleDataChangedListener = handleDataChangedListener;
}
public interface HandleDataChangedListener{
void onChanged();
}
In activity:
mListView.setHandleDataChangedListener(new CustomListView.HandleDataChangedListener() {
#Override
public void onChanged() {
mListView.setHandleDataChangedListener(null);
mListView.setSelection(0);
}
});
mAdapter.notifyDataSetChanged();
Ok, That's it.
In my case smoothScrollToPosition(int position) worked, can you also tell me how to set that scrolled position into center of the list. It appeared at the bottom of visible items.
For me it helped to set
ListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); or ListView.CHOICE_MODE_MULTIPLE
then
ListView.setSelection(position) or ListView.setItemChecked(position, true);
works fine
Found a solution in my case. I am not using a Runnable since my class is extending ListFragment. What I had to do is make my index a final;
final index = 5;
mListView.setSelection(index);
I found that sometimes setSelection will not work because I set attribute "android:height" of listView to "wrap_content".
And the times my App won't work is that when listView become scrollable from non-scrollable.
For example, if my app is "File Browser App". When my list is less than, let's say 6, then it's non-scrollable. Now I return to the parent directory, and it has 11 objects, and I want to set selection to some position, and it won't work here.
to\from | Scrollable | non-Scrollable
Scrollable | O | O( of course )
non-Scrollable | X | O( of course )
I don't want to use post(Runnable), because there will has delay.
==================================
Answer:
You can try to set "android:height" to "match_parent"
God, it spends three days.
When use post to setSelection(), the ListView will see first , then scroll to the position , thank to "魏經軒", then layout actually will effect the setSelection(), because setSelection() call the setSelectionFromTop(int position, int y), there is another way to solve it.
listView.setAdapter(listView.getAdapter());
listView.setSelection(123);
For me the solution to this problem was:
listView.clearChoices();
Simply try this code
listView.setAdapter(adapter);
listView.setSelection(position);
adapter.notifyDataSetChanged();

Click effect/animation for all application

It is possible to create a click effect/animation for all clicks on my application?
Since I want this behaviour for all clicks events on my application, how should I do this? Is this bad for performance or resources of the smartphone?
It is hard to know without understanding the stack that has been built, that being said I think there are some safer and less methodologies to an onclick event that is the same across the board. For one I would not change fundamental nature of the "onClick" function, the lower level you mess with the more dangerous it is. That being said I think I would create my own version/function of onclick, maybe boomClick, where boomClick creates the animation that you want. Referencing a single function will barely decrease performance at all.
So, after a day working at this I managed to accomplish the expected behaviour.
Basically, I create my own Activity class which will do the animation work with the help of a custom lib. I'll try to explain what I did for future reference:
1. Add this lib to your project:
compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.3'
2. Add these dimens to your "dimens.xml" file:
<dimen name="click_animation">100dp</dimen>
<dimen name="click_compensation">50dp</dimen>
3. Make the top parent of your activities layout a "RelativeLayout" and set a custom id:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/main_layout">
... the rest of the layout ...
</RelativeLayout>
4. Create your own "Activity" class:
public class MyActivity extends AppCompatActivity {
private RelativeLayout.LayoutParams params;
public void setClickAnimation(final Activity activity) {
// if you want to change the size of the animation, change the size on the dimens.xml
int size = (int) activity.getResources().getDimension(R.dimen.click_animation);
params = new RelativeLayout.LayoutParams(size, size);
// you want the parent layout of the activity
final RelativeLayout view = (RelativeLayout) activity.findViewById(R.id.main_layout);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// you maybe won't need this compensation value
int compensation = (int) activity.getResources().getDimension(R.dimen.click_compensation);
try { startAnimation(view, (int) event.getX() - compensation, (int) event.getY() - compensation); }
catch (IOException e) { e.printStackTrace(); }
}
return true;
}
});
}
private void startAnimation(RelativeLayout view, int x, int y) throws IOException {
params.leftMargin = x;
params.topMargin = y;
// those are from the lib you imported
final GifImageView anim = new GifImageView(this);
// if you don't have it yet, put the gif you want on the assets folder
final GifDrawable gifFromResource = new GifDrawable(getAssets(), "click_animation.gif");
gifFromResource.addAnimationListener(new AnimationListener() {
#Override
public void onAnimationCompleted(int loopNumber) {
anim.setVisibility(View.GONE);
gifFromResource.stop();
gifFromResource.recycle();
}
});
anim.setBackground(gifFromResource);
gifFromResource.start();
view.addView(anim, params);
}
}
5. Make your Activities extend your "Activity" class:
public class FirstScreen extends MyActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cards_screen);
// call the method you created and pass the activity context
setClickAnimation(this);
}
}
As for resources spent: this looks like a good solution and I am getting a good performance. The application seems to not be wasting a lot of resources with this solution.

Passing int from R.id.[value] as a class constructor argument gives an NPE when I use findViewById

When I simply try to create a button handling class that implements onTouch and onClick events for each button I run into an issue. The following combination works (ButtonClick is my class):
/*Constructor*/
public ButtonClick(View v, int clickType){
if(clickType == 0){
v.setOnClickListener(this);
}
else{
v.setOnTouchListener(this);
}
}
/*Call from Main.java*/
ButtonClick button = new ButtonClick(findViewById(R.id.button, 1);
However, the following gives me an NPE
/*Constructor*/
public ButtonClick(int buttonId, int clickType){
if(clickType == 0){
findViewById(buttonId).setOnClickListener(this);
}
else{
findViewById(buttonId).setOnTouchListener(this);
}
}
/*Call from Main.java*/
ButtonClick button = new ButtonClick(R.id.button, 1);
Why can you not pass the integer value from your R configuration file as an argument without it returning a NPE, at least in this case?
The problem is not with id, but with findViewById(). Before calling this method, the 'layout' should be 'inflated'. ie, Instantiate a layout XML file into its corresponding View objects. Inside an Activity, usually setContentView() do this job. Otherwise we can use LayoutInflater for the same purpose. Important point is that the inflater instance we get, is hooked up to the current context. So, inside another context, we get null Views.

Why cant i get these calculations to display on a textview?

Im trying to make an app that converts distance/area/volume using spinners as a unit selection method. Calculations are meant be done and then the output sent to a textview based on what is entered into the EditText. But the output is only ever 0.0 and nothing else. Can anybody help with this ?
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch(pos){
case 0:
option1.setAdapter(length);
option2.setAdapter(length);
return;
case 1:
option1.setAdapter(area);
option2.setAdapter(area);
return;
default:
}
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing.
}
});
option1.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if(pos>=0) {
stringInput = edittext1.getText().toString();
if(stringInput == null || stringInput.isEmpty()) {
doubleInput = 0.0;
}
else {
doubleInput = Double.parseDouble(edittext1.getText().toString());
}
}
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing.
}
});
option2.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch(pos) {
case 0:
miles = doubleInput * 1;
textview1.setText("" + String.valueOf(miles));
return;
case 1:
km = doubleInput * 1.609344;
textview1.setText("" + String.valueOf(km));
return;
default:
}
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing.
}
});
At the risk of sounding like a really old man, this looks like your college homework... Is it?
Your code has changed since asking the question, which makes it pretty difficult to answer! Luckily I managed to scrape your code into Eclipse before you changed it... Anyway, in your original code your performing all your operations in the create method, at which point you haven't entered a value for edittext1 (unless it's set to some sensible default, which I presume would be 0, hence always getting zero as your answer?)
// Whilst setting up a view the create method will not have a
// reasonable value for edittext1 - or it will be your default
String stringInput = (edittext1.getText().toString());
if (stringInput.isEmpty()) {
doubleInput = 0.0; // Will always enter this line
} else {
doubleInput = Double.parseDouble(edittext1.getText().toString());
}
You've duplicated the code...
output1 = (TextView) findViewById(R.id.output1);
Actually output1 through to output10 (ie all ten lines) are duplicated.
As to your updated code, is it still giving you a problem? Are you sure that stringInput has a value? I mean have you typed something in? You could check by debugging your program..
The following is also error prone as FloatingCoder suggests, and is likely to break...
doubleInput = Double.parseDouble(edittext1.getText().toString());
A better way to do this (because it catches the exception that Java might throw) is
doubleInput = 0.0;
String inputStr = question.getText().toString();
try {
doubleInput = Double.parseDouble(inputStr);
} catch (NumberFormatException e) {
// This should probably do something more useful? i.e. tell
// the user what they've done wrong...
Log.e("Android",
"Double throws a NumberFormatException if you enter text that is not a number");
}
Oh and Android has some helper utilities for checking strings, see TextUtils, or just my example...
if (!TextUtils.isEmpty(inputStr)) { // checks for "" and null (see documentation)
doubleInput = Double.parseDouble(inputStr);
}
I'd REALLY recommend writing a simple test case for a calculation that looks incorrect, because two text boxes and a button really aren't hard to throw together and are seriously easy to debug without the need for all the spinners getting in the way... Anyway hope this helped, oh and my complete example with two edittexts and a button, I'll just post that here... Hope it helps...
private Button btnCalc;
private EditText question, answer;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
answer = (EditText) findViewById(R.id.answer);
question = (EditText) findViewById(R.id.question);
btnCalc = (Button) findViewById(R.id.btnCalc);
// The OnClickListener here will be executed outside the "Create",
// i.e., when you actually click on the button, which will give you
// a chance to enter some values in the question edittext...
btnCalc.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
double in = 0.0;
try {
String inputStr = question.getText().toString();
// if you want to check it use
if (!TextUtils.isEmpty(inputStr)) {
in = Double.parseDouble(inputStr);
}
} catch (NumberFormatException e) {
// This should probably do something more useful? i.e. tell
// the user what they've done wrong...
Log.e("Android",
"Double throws a NumberFormatException if you enter text that is not a number");
}
double miles = in * 1.6;
answer.setText(String.valueOf(miles));
}
});
}
It's a little hard to tell from the code you posted, but my guess is that you are setting doubleInput when option 1 is selected. If you are entering the number after you select option 1, it will always be 0.0, as the number was not in the text area when the spinner was changed.
Use a debugger to step through the code and see if you are ever reaching the line
doubleInput = Double.parseDouble(edittext1.getText().toString());
and to check that the number is getting set properly at that point.

Categories

Resources