My program is supposed to have a listview, which when clicked, displays information for the clicked item. My problem is that I have attached the clicked item's name as an extra of the Intent that starts the new Activity. The information I need to display is stored as a string array. I need to use the string that I receive from the Intent to find the string array and bring it into Java. Can someone help me with this?
Thanks.
MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
public class MainActivity extends ActionBarActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listview = (ListView) findViewById(R.id.stationlist);
listview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
String[] stations = getResources().getStringArray(R.array.stations);
String station_name = stations[position] + "_timings";
Intent intent = new Intent(MainActivity.this, Station_timings.class);
intent.putExtra("stationwithtime", station_name);
intent.putExtra("position", position);
startActivity(intent);
};
});
}
#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;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Station_timings.class
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class Station_timings extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_station_timings);
String station_name = getIntent().getStringExtra("stationwithtime");
int position = getIntent().getIntExtra("position", 0);
TextView tv = (TextView) findViewById(R.id.textview);
Resources res = getResources();
String[] array = getResources().getStringArray(R.array.stations);
tv.setText(station_name);
int arrayid = res.getIdentifier(station_name, "array" , this.getPackageName());
String[] array2 = res.getStringArray(arrayid);
ListView listView = new ListView(this);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.activity_station_timings, array);
listView.setAdapter(adapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.station_timings, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="stations" style="#style/names_of_stations">
<item name="Oceanside">Oceanside Station</item>
<item name="San Clemente">San Clemente Station</item>
<item name="San Juan Capistrano">San Juan Capistrano Station</item>
<item name="Irvine">Irvine Station</item>
<item name="Tustin">Tustin Station</item>
<item name="Santa Ana">Santa Ana Station</item>
<item name="Orange">Orange Station</item>
<item name="Anaheim">Anaheim Station</item>
<item name="Fullerton">Fullerton Station</item>
<item name="Buena Park">Buena Park Station</item>
<item name="Santa Fe Springs/Norwalk">Santa Fe Springs/Norwalk Station</item>
<item name="Commerce">Commerce Station</item>
<item name="Los Angeles">Los Angeles Union Station</item>
</string-array>
<string-array name ="oceanside">
<item name="1"> 5:45 </item>
<item name="2"> 6:30 </item>
</string-array>
<string-array name ="station_timings">
<item name="1"> wrong </item>
<item name="2"> wrong </item>
</string-array>
<string name="title_activity_station_timings">Station_timings</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>
In your example, the variable station_name will contain the String "Oceanside Station". You are then calling
res.getIdentifier(station_name, "array" , this.getPackageName());
which will try to get a resource identifier for a string-array with the name "Oceanside Station". You've provided a string-array with the name "oceanside". They don't match.
Another thing you may wish to consider is using a more structured format to store your data, such as a JSON document, an XML document, a CSV file, or similar. You can then read that into your program as actual objects making them much easier to deal with.
Related
I do not know what error I have. I already put MainActivity between new Intent and this.
This is my mainactivity.java
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.:
Intent intent = new Intent(MainActivity.this, AboutActivity.class);
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
This is menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/about"
android:title="About"
app:showAsAction="never"/>
</menu>
This is AboutActivity.java
package com.example.zakatcalculator;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class AboutActivity extends AppCompatActivity {
TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
textView = findViewById(R.id.textViewLink);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
And this is AndroidManifest.xml
<activity
android:name=".AboutActivity"
android:exported="false">
</activity>
When I press about button, then its crash.
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.about:
Intent intent = new Intent(MainActivity.this, AboutActivity.class);
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
replace R.id. to this R.id.about and try again.
I created a bottom navigation bar with 3 icons and 3 different fragments linked to them.
On clicking an icon,
1.the icon changes
2. icon color changes
3. that related fragment appears on screen
On swipe,
only that fragment is changing.(icon is not changing).
Menu with 3 icons
<item
android:id="#+id/ic_birthdays"
android:title="#string/birthdays"
android:icon="#drawable/change_ic_cake"
/>
<item
android:id="#+id/ic_add"
android:title="#string/add"
android:icon="#drawable/change_ic_add"
/>
<item
android:id="#+id/ic_profile"
android:title="#string/profile"
android:icon="#drawable/change_ic_profile"
/>
The three icons linked in menu are:
1.Birthdays icon
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/ic_cake_clicked" />
<item android:state_checked="false" android:drawable="#drawable/ic_cake_not_clicked"/>
</selector>
2.Add Icon
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_add_not_clicked" android:state_checked="false"/>
<item android:drawable="#drawable/ic_add_clicked" android:state_checked="true"/>
</selector>
3.Profile icon
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_profile_not_clicked" android:state_checked="false"/>
<item android:drawable="#drawable/ic_profile_clicked" android:state_checked="true"/>
</selector>
MainActivity.java
package com.example.bottom_nav;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.example.bottom_nav.databinding.ActivityMainBinding;
import com.google.android.material.navigation.NavigationBarView;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.ViewGroup;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding ui;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ui = ActivityMainBinding.inflate(getLayoutInflater());
ViewGroup root = ui.getRoot();
setContentView(root);
//Instantiating 3 fragments
ViewPageAdapter viewPageAdapter = new ViewPageAdapter(this);
ui.viewPager.setAdapter(viewPageAdapter);
//Linking those three fragments to their respective icons of bottom nav bar
ui.bottomNav.setOnItemSelectedListener(
new NavigationBarView.OnItemSelectedListener(){
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.ic_birthdays:
ui.viewPager.setCurrentItem(0);
break;
case R.id.ic_add:
ui.viewPager.setCurrentItem(1);
break;
case R.id.ic_profile:
ui.viewPager.setCurrentItem(2);
break;
}
return true;
}
}
);
}
}
layout_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_nav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:menu="#menu/bottom_navigation_menu" />
</RelativeLayout>
ViewPageAdapter.java
package com.example.bottom_nav;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.example.bottom_nav.fragments.add;
import com.example.bottom_nav.fragments.birthdays;
import com.example.bottom_nav.fragments.profile;
public class ViewPageAdapter extends FragmentStateAdapter {
public ViewPageAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 1: return new add();
case 2: return new profile();
default: return new birthdays();
}
}
#Override
public int getItemCount() {
return 3;
}
}
The 3 fragments code
1.birthdays.java
package com.example.bottom_nav.fragments;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.bottom_nav.R;
public class birthdays extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_birthdays, container, false);
}
}
2.add.java
package com.example.bottom_nav.fragments;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.bottom_nav.R;
public class add extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_add, container, false);
}
}
3.profile.java
package com.example.bottom_nav.fragments;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.bottom_nav.R;
public class profile extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_add, container, false);
}
}
You need to have a listener on viewpager, and keep track of previous item selected,
so create a global variable,
Global variable:
MenuItem previousMenuItem;
then a viewpager listener:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (previousMenuItem != null) {
previousMenuItem.setChecked(false);
}
else {
mBottomNavigationView.getMenu().getItem(0).setChecked(false);
}
mBottomNavigationView.getMenu().getItem(position).setChecked(true);
previousMenuItem = mBottomNavigationView.getMenu().getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Don't forget to set a default item (first) to be selected of BottomNav on activity start.
I'm trying to add some menus to my app. here is my code:
java code:
package com.example.excercise;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Date;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
TextView my_text;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
my_text=findViewById(R.id.textView);
my_text.setText(new Date().toString());
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Random random=new Random();
my_text.setTextColor(Color.rgb(random.nextInt(256),random.nextInt(256),random.nextInt(256)));
}
});
button.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
return true;
}
});
}
#Override
protected void onResume() {
super.onResume();
Toast.makeText(this, "MainActivity: OnResume()", Toast.LENGTH_SHORT).show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu,menu);
return super.onCreateOptionsMenu(menu);
}
}
main_menu xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="Item1"/>
<item android:title="Item2"/>
<item android:title="Item3"/>
<item
android:id="#+id/app_bar_search"
android:icon="#drawable/ic_search_black_24dp"
android:title="Search"
app:actionViewClass="android.widget.SearchView"/>
<item android:title="Item4">
<menu>
<item android:title="SubItem1"
android:onClick="itemClick"/>
<item android:title="SubItem2"/>
</menu>
</item>
I'm getting this message in red in logcat :
2019-07-14 02:49:18.148 6160-6160/com.example.excercise
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.excercise, PID: 6160
android.view.InflateException: Couldn't resolve menu item onClick handler itemClick in class com.example.excercise.MainActivity
at androidx.appcompat.view.SupportMenuInflater$InflatedOnMenuItemClickListener.(SupportMenuInflater.java:254)
at androidx.appcompat.view.SupportMenuInflater$MenuState.setItem(SupportMenuInflater.java:482)
at androidx.appcompat.view.SupportMenuInflater$MenuState.addItem(SupportMenuInflater.java:530)
at androidx.appcompat.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:206)
at androidx.appcompat.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:184)
at androidx.appcompat.view.SupportMenuInflater.inflate(SupportMenuInflater.java:128)
at com.example.excercise.MainActivity.onCreateOptionsMenu(MainActivity.java:69)
at android.app.Activity.onCreatePanelMenu(Activity.java:4055)
at androidx.fragment.app.FragmentActivity.onCreatePanelMenu(FragmentActivity.java:378)
at androidx.appcompat.view.WindowCallbackWrapper.onCreatePanelMenu(WindowCallbackWrapper.java:94)
at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.onCreatePanelMenu(AppCompatDelegateImpl.java:2549)
at androidx.appcompat.app.AppCompatDelegateImpl.preparePanel(AppCompatDelegateImpl.java:1589)
at androidx.appcompat.app.AppCompatDelegateImpl.doInvalidatePanelMenu(AppCompatDelegateImpl.java:1869)
at androidx.appcompat.app.AppCompatDelegateImpl$2.run(AppCompatDelegateImpl.java:230)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7319)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
Caused by: java.lang.NoSuchMethodException: com.example.excercise.MainActivity.itemClick [interface
android.view.MenuItem]
at java.lang.Class.getMethod(Class.java:2072)
at java.lang.Class.getMethod(Class.java:1693)
at androidx.appcompat.view.SupportMenuInflater$InflatedOnMenuItemClickListener.(SupportMenuInflater.java:250)
at androidx.appcompat.view.SupportMenuInflater$MenuState.setItem(SupportMenuInflater.java:482)
at androidx.appcompat.view.SupportMenuInflater$MenuState.addItem(SupportMenuInflater.java:530)
at androidx.appcompat.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:206)
at androidx.appcompat.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:184)
at androidx.appcompat.view.SupportMenuInflater.inflate(SupportMenuInflater.java:128)
at com.example.excercise.MainActivity.onCreateOptionsMenu(MainActivity.java:69)
at android.app.Activity.onCreatePanelMenu(Activity.java:4055)
at androidx.fragment.app.FragmentActivity.onCreatePanelMenu(FragmentActivity.java:378)
at androidx.appcompat.view.WindowCallbackWrapper.onCreatePanelMenu(WindowCallbackWrapper.java:94)
at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.onCreatePanelMenu(AppCompatDelegateImpl.java:2549)
at androidx.appcompat.app.AppCompatDelegateImpl.preparePanel(AppCompatDelegateImpl.java:1589)
at androidx.appcompat.app.AppCompatDelegateImpl.doInvalidatePanelMenu(AppCompatDelegateImpl.java:1869)
at androidx.appcompat.app.AppCompatDelegateImpl$2.run(AppCompatDelegateImpl.java:230)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7319)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
what is the problem and how should I fix it? I should add that in all java classes that red message refer me to, I get the "cannot resolve R symbol" error.
In your main_menu.xml add an id to the item element
<?xml version="1.0" encoding="utf-8"?>
<item android id="#+id/item1"
android:title="Item1"/>
<item android id="#+id/item2"
android:title="Item2"/>
<item android id="#+id/item3"
android:title="Item3"/>
In your MainActivity.java add onOptionsItemSelected method
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu,menu);
return super.onCreateOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
//respond to menu item selection
switch (item.getItemId()) {
case R.id.item1:
Toast.makeText(this, "You clicked on item 1", Toast.LENGTH_SHORT).show();
return true;
case R.id.item2:
Toast.makeText(this, "You clicked on item 1", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Finally to solve the cannot resolve R symbol error...You have not imported the R class into your java file.There are two steps to do that
Step One
Click on the R symbol you should see a red bulb...click on that red bulb...you'd see a popup showing import class...Click on import
Or Step Two
Click on the R symbol...Then Press Alt + Enter
I hope this helps...
I am using a Listfragment which has a checkbox with custom images that changes on state_checked. But 'onListItemClick' function works only when checkbox is assigned to android:focusable="false" and android:clickable="false" in its layout.
In this case the custom image change of checkbox is not working.Is there a way to accomplish both together.Any help is appreciated.
public class SympFragment extends ListFragment implements OnClickListener {
private ListView lv;
private String listview_array[] = {
"Text1",
"Text2",
"Text3",
"Text4",
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_symp, container, false);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
R.layout.ch_row, listview_array);
setListAdapter(adapter);
return rootView;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
switch( position )
{
case 0: Toast.makeText(getActivity().getApplicationContext(), "Well Done!", Toast.LENGTH_LONG).show();
break;
}
}
}
Layout:
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/chcheckbox"
android:textSize="15sp"
android:textColor="#ffffff"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:button="#drawable/chselector"
android:drawableLeft="#android:color/transparent"
android:focusable="false"
android:clickable="false"
android:padding="8dp"
/>
Selector xml:
<?xml version="1.0" encoding="utf-8"?>
<item android:drawable="#drawable/unchecked" android:state_checked="false"/>
<item android:drawable="#drawable/checked" android:state_checked="true"/>
<item android:drawable="#drawable/unchecked"/>
create xml file custom_checkbox.xml in drawable :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/unselected" android:state_checked="false"/>
<item android:drawable="#drawable/selected" android:state_checked="true"/>
<item android:drawable="#drawable/unselected"/>
</selector>
and in checkbox call :
android:button="#drawable/custom_checkbox"
Seems like 'onListItemClick' never accepts checkbox input. So the checkbox has to be setted to android:focusable="false" and android:clickable="false".
But checkbox can be triggered by the following way :
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
String item = (String) getListAdapter().getItem(position);
Toast.makeText(getActivity(), item + " selected", Toast.LENGTH_LONG).show();
CheckBox chcheckbox;
switch(position){
case 0:
chcheckbox = (CheckBox) v.findViewById(R.id.chcheckbox);
if (chcheckbox.isChecked() == false) {
chcheckbox.setChecked(true);
} else {
chcheckbox.setChecked(false);
} break;
case 1:
chcheckbox = (CheckBox) v.findViewById(R.id.chcheckbox);
if (chcheckbox.isChecked() == false) {
chcheckbox.setChecked(true);
} else {
chcheckbox.setChecked(false);
} break;
}
}
Am not sure whether this is a best practice. But this tweak is much useful as I didn't find any other solution.
I've got a fairly simple app I wrote to sharpen my JNI and multi-threading skills which takes either a string of ascii characters to convert to a binary string or a binary character string to convert to an ascii character string. The math behind each conversion is processed via a native lib, and each call to the native code runs on its own thread using AsyncTask so as not to disrupt the UI. The trouble is that whenever onLayout is called when the UI widgets need to change size or position, the new image is placed over top of the still visible old image rather than replacing it. I've tried removing likely culprits (the JNI module, the AsyncTask module) but even when its functionality is stripped and only the UI remains I see the same result of new UI views overlapping instead of replacing older views. As you can see from my code below, I've also variably tried the old setContentView(...) approach to UI hierarchy modeling in addition to the more flexible custom addView model I have active now, but the results were still the same. NOTE: This only happens on device (Nexus 7 running latest 4.2.1 and Nexus S running 4.0.2); in the emulator everything works fine. This would seem to implicate GLES invocations, but I don't make any of my own in this app and obviously this isn't happening to every app on device so it doesn't seem like an OS issue...
Activity Class:
package cresco.ai.asciitobinstring.core;
import android.app.Activity;
import android.os.Bundle;
public class AsciiToBinActivity_Minimalist extends Activity{
private AsciiToBin_Main hMain;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
hMain = new AsciiToBin_Main(this);
hMain.init();
}
#Override
protected void onResume(){
super.onResume();
}
#Override
protected void onPause(){
super.onPause();
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
}
#Override
protected void onRestart(){
super.onRestart();
}
#Override
protected void onDestroy(){
super.onDestroy();
}
}
Main manager class:
package cresco.ai.asciitobinstring.core;
import cresco.ai.asciitobinstring.gui.ConversionViewController;
import cresco.ai.asciitobinstring.gui.DefineLayoutParams;
import cresco.ai.asciitobinstring.gui.TutorialViewController;
import cresco.ai.asciitobinstring.math.StringConverter;
import android.app.Activity;
import android.content.res.AssetManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.ViewFlipper;
public class AsciiToBin_Main {
private Activity hAct;
private View mvMainView;
private ViewFlipper mvRootViewFlipper;
private ConversionViewController mvConversionViewController;
private TutorialViewController mvTutorialViewController;
private RelativeLayout mvRootRL;
private AssetManager mvAssetManager;
private LayoutInflater mvInflater;
private StringConverter mvConverter;
public AsciiToBin_Main(Activity act){
hAct = act;
mvConverter = new StringConverter(this);
mvRootRL = new RelativeLayout(hAct);
hAct.addContentView(mvRootRL,
DefineLayoutParams.getParams(DefineLayoutParams.getMM()));
//hAct.setContentView(mvRootRL);
///hAct.setContentView(R.layout.conversion_layout);
}
public void init(){
//Add the viewgroup subclass viewflipper to the rootRL
mvRootViewFlipper = new ViewFlipper(hAct);
mvRootRL.addView(mvRootViewFlipper);
//Instantiate our conversion view controller
mvConversionViewController = new ConversionViewController(this);
mvTutorialViewController = new TutorialViewController(this);
//Fire up the conversion view
mvConversionViewController.initGUI();
mvConversionViewController.initSpinnerElements();
//Fire up the tutorial view
mvTutorialViewController.initGUI();
}
public RelativeLayout getRootRL(){
return mvRootRL;
}
public ViewFlipper getRootViewFlipper(){
return mvRootViewFlipper;
}
public View getMainView(){
return mvMainView;
}
public Activity getAct(){
return hAct;
}
public void setMainView(View v){
mvMainView = v;
}
public void setActivity(Activity a){
hAct = a;
}
public StringConverter getConverter(){
return mvConverter;
}
public void setConverter(StringConverter sc){
mvConverter = sc;
}
}
Conversion View Controller:
package cresco.ai.asciitobinstring.gui;
import cresco.ai.asciitobinstring.core.AsciiToBin_Main;
import cresco.ai.asciitobinstring.core.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
public class ConversionViewController extends View implements OnClickListener,
OnItemSelectedListener{
private AsciiToBin_Main hMain;
private LayoutInflater mvInflater;
private View mvConversionView;
private Button mvConvertB;
private Button mvToTutorialB;
private Spinner mvConversionTypeSP;
private EditText mvAsciiStringET;
private EditText mvBinStringET;
private ArrayAdapter<CharSequence> mvSpinnerAdapter;
public ConversionViewController(AsciiToBin_Main main) {
super(main.getAct());
hMain = main;
}
public void initGUI(){
mvInflater = (LayoutInflater)
hMain.getAct().getSystemService(hMain.getAct().LAYOUT_INFLATER_SERVICE);
mvConversionView = mvInflater.inflate(R.layout.conversion_layout,
hMain.getRootViewFlipper(),false);
hMain.getRootViewFlipper().addView(mvConversionView);
//hMain.getRootRL().addView(mvConversionView);
mvConvertB = (Button)hMain.getAct().findViewById(R.id.convertB);
mvToTutorialB =
(Button)hMain.getAct().findViewById(R.id.conversion_To_Tutorial_B);
mvAsciiStringET =
(EditText)hMain.getAct().findViewById(R.id.asciiStringET);
mvBinStringET = (EditText)hMain.getAct().findViewById(R.id.binStringET);
mvConversionTypeSP =
(Spinner)hMain.getAct().findViewById(R.id.conversionTypeSP);
mvConvertB.setOnClickListener(this);
mvToTutorialB.setOnClickListener(this);
mvConversionTypeSP.setOnItemSelectedListener(this);
}
public void initSpinnerElements(){
mvSpinnerAdapter = ArrayAdapter.createFromResource(hMain.getAct(),
R.array.conversion_choices_array, android.R.layout.simple_spinner_item);
mvSpinnerAdapter.setDropDownViewResource
(android.R.layout.simple_spinner_dropdown_item);
mvConversionTypeSP.setAdapter(mvSpinnerAdapter);
mvConversionTypeSP.setSelection(0);
//Now that that the UI is ready, display it
////hMain.getRootViewFlipper().showNext();
}
#Override
protected void onDraw(Canvas c){
super.onDraw(c);
}
#Override
public void onClick(View v) {
if(v==mvConvertB){
Log.d("me", "button pressed! Conversion type selected currently is
"+mvSpinnerAdapter.getItem(mvConversionTypeSP.getSelectedItemPosition()));
if(mvConversionTypeSP.getSelectedItemPosition() == 0){
//This is the convert ascii to binary string choice
//Calls the native method calculateBinFromAsciiStringJNI
//Not a terribly heavy process, but since it is disparate
//from
//the UI we should probably grant it its own thread...
//Uncomment when UI overlay bug is solved
new
NativeStringConversionTask(0).execute(mvAsciiStringET.getText().toString());
//mvBinStringET.setText(hMain.getConverter().
//calculateBinFromAsciiStringJNI(mvAsciiStringET
//.getText().toString())); //all on the UI thread, not good
}
else if(mvConversionTypeSP.getSelectedItemPosition() == 1){
//This is the convert binary to ascii string choice
//Uncomment when UI overlay bug is solved
new
NativeStringConversionTask(1).execute(mvBinStringET.getText().toString());
//mvAsciiStringET.setText(hMain.getConverter().
//calculateAsciiFromBinStringJNI(mvBinStringET
//.getText().toString())); //all on the UI thread. Not good
}
}
else if(v==mvToTutorialB){
//hMain.getRootViewFlipper().setDisplayedChild(1);
hMain.getRootViewFlipper().showNext();
//hMain.getAct().setContentView(R.layout.tutorial_layout);
}
}
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Log.d("me", "itemselectedlistener received a callback! the position
selected is "+arg2);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
Log.d("me", "itemselectedlistener received a no items slected callaback");
}
//Uncomment once the UI overlay issue is solved
private class NativeStringConversionTask extends AsyncTask<String,Integer,String>{
private int taskID = 0;
public NativeStringConversionTask(int id){
taskID = id;
}
#Override
protected void onPreExecute(){
Log.d("me","in the preExecute of the nativeStringConversion Async
task");
}
#Override
protected String doInBackground(String... s) {
//NB: if we wanted onProgressUpdate to be called with something we
//would
//invoke publishProgress(something) in this function
Log.d("me", "bg task started with taskID "+taskID);
if(taskID == 0){
publishProgress(taskID);
return
hMain.getConverter().calculateBinFromAsciiStringJNI(s[0].toString());
}
else if (taskID == 1){
publishProgress(taskID);
return
hMain.getConverter().calculateAsciiFromBinStringJNI(s[0].toString());
}
else{
publishProgress(taskID);
return "This shouldn't appear. if it does... may the
gods below help us all";
}
}
#Override
protected void onProgressUpdate(Integer... progress){
Log.d("me", "progressUpdate called!");
}
#Override
protected void onPostExecute(String result){
Log.d("me", "calling postExecute...");
if(taskID == 0){
mvBinStringET.setText((String)result);
}
else if (taskID == 1){
mvAsciiStringET.setText((String)result);
}
else{
mvBinStringET.setText("This shouldn't appear. if it
does... may the gods below help us all");
mvAsciiStringET.setText("This shouldn't appear. if it
does... may the gods below help us all");
}
//mvBinStringET.setText(s);
}
}
}
Tutorial View Controller:
package cresco.ai.asciitobinstring.gui;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import cresco.ai.asciitobinstring.core.AsciiToBin_Main;
import cresco.ai.asciitobinstring.core.R;
public class TutorialViewController extends View implements OnClickListener{
private AsciiToBin_Main hMain;
private LayoutInflater mvInflater;
private View mvTutorialView;
private Button mvToConversionB;
public TutorialViewController(AsciiToBin_Main main){
super(main.getAct());
hMain = main;
}
public void initGUI(){
mvInflater = (LayoutInflater)
hMain.getAct().getSystemService(hMain.getAct().LAYOUT_INFLATER_SERVICE);
mvTutorialView = mvInflater.inflate(R.layout.tutorial_layout,
hMain.getRootViewFlipper(),false);
hMain.getRootViewFlipper().addView(mvTutorialView);
mvToConversionB =
(Button)hMain.getAct().findViewById(R.id.tutorial_Conversion_B);
mvToConversionB.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v==mvToConversionB){
//hMain.getRootViewFlipper().setDisplayedChild(0);
hMain.getRootViewFlipper().showNext();
//hMain.getAct().setContentView(R.layout.conversion_layout);
}
}
}
StringConverter:
package cresco.ai.asciitobinstring.math;
import cresco.ai.asciitobinstring.core.AsciiToBin_Main;
public class StringConverter {
private AsciiToBin_Main hMain;
public StringConverter(AsciiToBin_Main main){
hMain = main;
}
//Uncomment once the UI overlay issue is solved
public native String calculateBinFromAsciiStringJNI(String s);
public native String calculateAsciiFromBinStringJNI(String s);
static {
System.loadLibrary("stlport_shared");
System.loadLibrary("ascii2bin");
}
}
conversion_layout.xml:
<?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" >
<EditText
android:id="#+id/asciiStringET"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/conversionTypeSP"
android:layout_centerHorizontal="true"
android:layout_marginTop="71dp"
android:hint="Your ASCII string goes here"
android:text="" />
<EditText
android:id="#+id/binStringET"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/asciiStringET"
android:layout_below="#+id/asciiStringET"
android:layout_marginTop="61dp"
android:hint="Your binary string goes here"
android:text="" />
<Button
android:id="#+id/convertB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="#+id/binStringET"
android:text="Convert Strings!" />
<Button
android:id="#+id/conversion_To_Tutorial_B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="#+id/convertB"
android:text="Read the tutorial on binary numbers" />
<Spinner
android:id="#+id/conversionTypeSP"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:layout_marginTop="57dp" />
</RelativeLayout>
tutorial_layout.xml:
<?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" >
<Button
android:id="#+id/tutorial_Conversion_B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="204dp"
android:text="Return to Conversion" />
</RelativeLayout>
Manifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cresco.ai.asciitobinstring.core"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="cresco.ai.asciitobinstring.core.AsciiToBinActivity_Minimalist"
android:label="#string/app_name"
android:theme="#style/FullscreenTheme" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The results I'm seeing are as follows:
Now what's curious is that if I put the app in the background and then re-open it (so onPause and onResume are called) the UI that loads is refreshed properly, with old view artifacts removed. Of course attempting a new string conversion that resizes the edittext widgets creates the overlap again.
Does anyone know what might be behind this? I am targeting sdk revision 17, standard libs with a minSDK of 8. When I first saw this I thought it was a weird side-effect of the new-ish holo UI scheme, so I rolled back the styles.xml to a 2.3 compliant UI scheme but I didn't see any improvement. could be that messed something up separately...
this isn't really a good answer per se because it certainly doesn't explain what happened in the first place, but I found that creating a new project from scratch fixed the odd errors I was seeing; therefore the problem was most likely caused by my mucking about with the UI styles... wish I had a more technical explanation than that, but at present I don't. Anyway, if anyone else runs into this, re-creating your project seems to clear the problem. If anyone has more explanatory details they'd like to add, please feel free!