I've created a layout with a menu view that will be in other layouts showing some buttons. To define these buttons and their actions, I'm creating an independet class to define all of them and this way I avoid to have to define all of them again and again in all the activities.
To do this, first I've created the menu_view layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<!-- Button columns -->
<RelativeLayout
android:id="#+id/border_left"
android:layout_width="100dip"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_alignParentLeft="true"
android:background="#000000" >
<ImageView
android:id="#+id/radioButton"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_weight="1"
android:layout_marginTop="60dp"
android:layout_marginLeft="10dp"
android:onClick="launch_radio"
android:src="#drawable/radio_button" />
<ImageView
android:id="#+id/musicButton"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_weight="1"
android:layout_marginTop="200dp"
android:layout_marginLeft="10dp"
android:onClick="launch_media"
android:src="#drawable/music_button" />
</RelativeLayout>
</RelativeLayout>
NOTE: Here aren't all the buttons, I only put 2 to have an idea.
After this, I created the MenuView class where I define all these buttons and what they do:
public class MenuView extends RelativeLayout {
private final LayoutInflater inflater;
public MenuView(Context context, AttributeSet attrs) {
super(context, attrs);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.menu_view, this, true);
((ImageView)this.findViewById(R.id.navButton)).setOnClickListener(launch_nav);
((ImageView)this.findViewById(R.id.phoneButton)).setOnClickListener(launch_phone);
((ImageView)this.findViewById(R.id.webButton)).setOnClickListener(launch_web);
((ImageView)this.findViewById(R.id.backButton)).setOnClickListener(goBack);
}
private final OnClickListener launch_nav = new OnClickListener() {
#Override
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), Navigation.class));
}
};
**¡¡¡THROUBLE!!!**
private final OnClickListener launch_phone = new OnClickListener() {
#Override
public void onClick(View v) {
public void launch_phone (String number) {
String numberToDial = "tel:"+number;
}
getContext().startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(numberToDial)));
}
};
private final OnClickListener launch_web = new OnClickListener() {
#Override
public void onClick(View v) {
getContext().startActivity(new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://www.google.es")));
}
};
**¡¡¡THROUBLE!!!**
private final OnClickListener goBack = new OnClickListener() {
#Override
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), NO.class));
}
};
}
Here is where I'm a little bit lost. I have to define 2 actions:
1- The phone dialer
2- The goBack key
The phone dialer I don't know how to define it right here. I had defined in other way when I was first creating this app, and it worked fine.. But now, with lot of activities, I have to do this way, and here it shows an error because this isn't the way to do it.
With the back button, what I need is just to define that when this button is pressed, it must go back on activity, but again, in this type of class I don't know how to define it.
It sounds as though with a quick overview of your application that you are segregating code outside of an Activity where it originally existed and now what these to be structured so that they are in a separate class object to be referenced. You are already passing in a context when you construct the class, so you should be able to just save that context that is being passed in and use that as your launch for the goBack call. Otherwise you are calling a goBack on a view context, and the back button is associated with an Activity (which is ideally what you have passed into this class when you created it) so you want the context passed in to your view.
private activityContext;
public MenuView(Context context, AttributeSet attrs) {
super(context, attrs);
activityContext = context;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.menu_view, this, true);
((ImageView)this.findViewById(R.id.navButton)).setOnClickListener(launch_nav);
((ImageView)this.findViewById(R.id.phoneButton)).setOnClickListener(launch_phone);
((ImageView)this.findViewById(R.id.webButton)).setOnClickListener(launch_web);
((ImageView)this.findViewById(R.id.backButton)).setOnClickListener(goBack);
}
**¡¡¡THROUBLE!!!**
private final OnClickListener goBack = new OnClickListener() {
#Override
public void onClick(View v) {
activityContext.startActivity(new Intent(activityContext, NO.class));
}
};
}
Related
When I use onRetainCustomNonConfigurationInstance to save the text contained inside my custom EditTexts, after I rotate the device the text contained in the last EditText gets duplicated to all the others in the target layout.
While debugging with breakpoints, I found that all the text values are correct all the way. But after the changes are done, all the EditTexts get the same text showing (the text from the last one) and the focus is given to the first one in the layout.
I replicated this behavior in the simplest project I could. I tried with both android API levels 24 and 28.
Where does this behavior come from and how can I fix it ?
MainActivity.java :
public class MainActivity extends AppCompatActivity {
private ArrayList<CustomEdit> editList=new ArrayList<CustomEdit>();
private LinearLayout layout;
private Button addButton;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addButton=findViewById(R.id.add_button);
layout = findViewById(R.id.layout);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addEdit();
}
});
CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
if(data==null) return;
for(int i = 0; i<data.texts.size();i++){
addEdit(data.texts.get(i));
}
}
#Override
public Object onRetainCustomNonConfigurationInstance() {
CustomSave data = save();
return data;}
private CustomSave save(){
ArrayList<String> texts = new ArrayList<String>();
for(int i =0; i<editList.size(); i++)
texts.add(editList.get(i).getText());
return new CustomSave(texts);}
/**
* Create a new custom EditText with hint
*/
private void addEdit(){
CustomEdit newEdit = new CustomEdit(this,editList.size());
layout.addView(newEdit,editList.size());
editList.add(newEdit);}
/**
* Create a new custom editText with text
* #param text
*/
private void addEdit(String text){
CustomEdit newEdit;
if(text==null) newEdit = new CustomEdit(this, editList.size());
else newEdit = new CustomEdit(this, editList.size(),text);
layout.addView(newEdit,editList.size());
editList.add(newEdit);}
}
activity_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">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="20dp"
android:text="Title"
android:gravity ="center"/>
<HorizontalScrollView
android:id="#+id/scroll1"
android:layout_below="#id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingLeft="10dp">
<LinearLayout
android:id="#+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/add_button"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="+"/>
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
CustomEdit.java :
public class CustomEdit extends RelativeLayout {
private EditText editText;
private Button closeButton;
private int indexNumber;
public CustomEdit(Context context, int indexNumber) {
super(context);
this.indexNumber =indexNumber;
init();
}
public CustomEdit(Context context, int indexNumber, String text){
super(context);
this.indexNumber=indexNumber;
init();
editText.setText(text);
}
public CustomEdit(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomEdit(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
init();
}
private void init(){
inflate(getContext(),R.layout.custom_edit_text,this);
editText = (EditText)findViewById(R.id.edit);
editText.setHint("EditText "+(indexNumber+1));
closeButton = (Button)findViewById(R.id.close_button);
closeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity)getContext()).closeEdit(indexNumber);
}
});
}
public String getText(){
return editText.getText().toString();
}
}
custom_edit_text.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="40dp">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/edit"
android:inputType="text"
android:hint="Element"
android:maxLength="30"/>
<Button
android:id="#+id/close_button"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignRight="#id/edit"
android:layout_alignEnd="#id/edit"
android:layout_alignTop="#id/edit"
android:text="X"/>
</RelativeLayout>
CustomSave.java :
public class CustomSave {
public ArrayList<String> texts;
CustomSave(ArrayList<String> texts){
this.texts = texts;
}
}
Thank you.
You have two choices for how to solve this: move your code to a different point in the activity lifecycle or change the xml definition for CustomEdit.
Android lifecycle
Move this code out of onCreate():
CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
if(data==null) return;
for(int i = 0; i<data.texts.size();i++){
addEdit(data.texts.get(i));
}
And put it in onRestoreInstanceState() instead:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
CustomSave data = (CustomSave)getLastCustomNonConfigurationInstance();
if(data==null) return;
for(int i = 0; i<data.texts.size();i++){
addEdit(data.texts.get(i));
}
}
or
XML definition
Add this attribute to the <EditText> tag in your custom_edit_text.xml file:
android:saveEnabled="false"
There's nothing wrong with the code you've written to save/restore your text values. However, after onCreate(), Android is automatically performing its own save/restore logic, and that's overwriting what you've done.
If you move your code from onCreate() to onRestoreInstanceState(), then your code will run after Android's automatic save/restore, so you'll "win". Or, you can disable the automatic save/restore by adding the saveEnabled=false attribute.
The reason that Android's automatic save/restore doesn't work is that it is based on each view's android:id attribute, and your EditText tags all have the same id. That means that all four values get saved with the same key, so the last value overwrites all previous values.
So what's happening here is that Android is also handling state saving for your custom EditText implementation, which is overwriting yours. Since your list of CustomEdit instances is generated dynamically, you likely do not want to rely on Android to save the state for your views, since they do not have unique IDs.
Since your CustomEdit inflates custom_edit_text.xml (which declares the EditText ID as #+id/edit) that means that each CustomEdit you add to the layout has the same ID for the inner EditText -- R.id.edit. Since they all have the same ID, each view will save its state to that ID, so the last one to save its state will end up being the text applied to all of the views when restoring state.
There are two things you can do to avoid this:
In your custom_edit_text.xml, add android:saveEnabled="false" to the EditText. This will prevent that View's state from being saved. This would be preferred as it avoids doing unnecessary work.
Perform your state restoration in onRestoreInstanceState() where the view state is being restored currently.
I am using the Support Library to add a bottom bar similar to the material design one. The bottom bar works great but it seems that if I have the bar displayed, if I try to open any fragment from my custom adapter, the fragment does not open...or maybe it opens behind my main layout? I have no idea how to figure this out. Below is my code.
I've read more posts on SO and around the web and I think this is related to the fragment being properly loaded but below or next to the bottom bar...and that is why it isn't visible? Why does this happen? Is it because the bottom bar has a LinearLayout? I defined it as a menu so I'm not sure if I can control it being a LinearLayout....
Setting up the bottom bar, this method is called from the onCreate of my activity:
public void setupBottomToolbar(Bundle savedInstanceState) {
mBottomBar = BottomBar.attach(MainActivity.this, savedInstanceState);
mBottomBar.setItems(R.menu.bottombar_menu);
mBottomBar.setOnMenuTabClickListener(new OnMenuTabClickListener() {
#Override
public void onMenuTabSelected(#IdRes int menuItemId) {
if (menuItemId == R.id.toolbar_jobs) {
} else if (menuItemId == R.id.toolbar_messages) {
} else if (menuItemId == R.id.toolbar_recentJobs) {
} else if (menuItemId == R.id.toolbar_employerPools) {
}
}
#Override
public void onMenuTabReSelected(#IdRes int menuItemId) {
if (menuItemId == R.id.toolbar_jobs) {
// The user reselected item number one, scroll your content to top.
} else if (menuItemId == R.id.toolbar_messages) {
} else if (menuItemId == R.id.toolbar_employerPools) {
} else if (menuItemId == R.id.toolbar_recentJobs) {
}
}
});
// Setting colors for different tabs when there's more than three of them.
// You can set colors for tabs in three different ways as shown below.
mBottomBar.getBar().setBackgroundColor(getResources().getColor(R.color.laborswipe_darkgray));
mBottomBar.setActiveTabColor(getResources().getColor(R.color.laborswipe_lightgray));
// Make a Badge for the second tab, with red background color and a value of "13".
BottomBarBadge unreadMessages = mBottomBar.makeBadgeForTabAt(1, getResources().getColor(R.color.laborswipe_orange), 5);
// Control the badge's visibility
unreadMessages.show();
//unreadMessages.hide();
// Change the displayed count for this badge.
//unreadMessages.setCount(4);
// Change the show / hide animation duration.
unreadMessages.setAnimationDuration(200);
// If you want the badge be shown always after unselecting the tab that contains it.
unreadMessages.setAutoShowAfterUnSelection(true);
// If you don't want this badge to be hidden after selecting the tab contains it.
unreadMessages.setAutoShowAfterUnSelection(false);
}
In my adapter, I am trying to open the fragment when you click a button, like this:
holder.desc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Open Description", Toast.LENGTH_SHORT).show();
JobDescFragment firstFragment = new JobDescFragment();
((MainActivity)context).getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
});
If I comment out the call to setupBottomToolbar() in my onCreate of the activity, the fragment opens up fine...but that means I don't have the bottom bar...
What am I missing? There has to be a way to use the bottom bar and also open a fragment?
Thanks!
EDIT:
Here is the top part of my activity.
public class MainActivity extends AppCompatActivity {
private ArrayList<String> swipecardsList;
private ArrayList<Job> jobList = new ArrayList<Job>();
private JobAdapter arrayAdapter; //arrayadapter
private BottomBar mBottomBar;
SharedPreferences settings;
#InjectView(R.id.frame) SwipeFlingAdapterView flingContainer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Remove title bar
//this.requestWindowFeature(Window.FEATURE_NO_TITLE);
//color the notification bar with our company colors
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(this.getResources().getColor(R.color.laborswipe_notificationbar));
//remove title from action bar and add the logo to the top left of the action bar
setupTopToolbar();
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
//set up the bottom toolbar using the roughike library to mimic android material design
setupBottomToolbar(savedInstanceState);
My adapter:
public class JobAdapter extends ArrayAdapter<Job> {
private final Context context;
private final ArrayList<Job> jobs;
private final int layoutResourceId;
private final SwipeFlingAdapterView flingContainer;
private boolean isExpanded = false;
public JobAdapter(Context context, int layoutResourceId, ArrayList<Job> jobs, SwipeFlingAdapterView flingContainer) {
super(context, layoutResourceId, jobs);
this.context = context;
this.jobs = jobs;
this.layoutResourceId = layoutResourceId;
this.flingContainer = flingContainer;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
final ViewHolder holder;
String pay, hrs;
final Bundle fragmentParams = new Bundle();
LayoutInflater inflater = LayoutInflater.from(context);
if (view == null) {
view = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.title = (TextView)view.findViewById(R.id.tv_jobTitle);
holder.desc = (TextView) view.findViewById(R.id.tv_JobDesc);
view.setTag(holder);
} else {
holder = (ViewHolder)view.getTag();
}
Job j = jobs.get(position);
holder.title.setText(j.getJobTitle());
holder.desc.setText(j.getDescription());
//when user clicks apply, swipe the card right
holder.apply.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Open up a fragment to display the entire job description
Toast.makeText(context, "Applied", Toast.LENGTH_SHORT).show();
flingContainer.getTopCardListener().selectRight();
}
});
//when user clicks dismiss, swipe the card left
holder.dismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Open up a fragment to display the entire job description
Toast.makeText(context, "Dismissed", Toast.LENGTH_SHORT).show();
flingContainer.getTopCardListener().selectLeft();
}
});
//on click event listener for the job description field - open larger window to read description
holder.desc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Toast.makeText(context, "Open Description", Toast.LENGTH_SHORT).show();
JobDescFragment firstFragment = new JobDescFragment();
Fragment frag = new Fragment();
frag = firstFragment.newJobDescFrag(j.getDescription());
((MainActivity) context).getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, frag)
.addToBackStack("JobDesc").commit();
}
});
return view;
}
public class ViewHolder
{
TextView title;
TextView payrate;
TextView dateRange;
TextView workinghrs;
TextView location;
TextView companyname;
TextView desc;
TextView experience;
TextView equipment;
Button apply, dismiss, expand;
}
}
activity_main.xml:
<merge
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">
<com.lorentzos.flingswipe.SwipeFlingAdapterView
android:id="#+id/frame"
android:background="#color/laborswipe_lightgray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:layout_gravity="top" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>
Fragment Layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".JobDescFragment">
<LinearLayout
android:id="#+id/outerDescriptionLayout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical"
android:background="#drawable/swipecard_shadow"
android:gravity="top"
android:layout_marginLeft="5dp">
<LinearLayout
android:id="#+id/DescriptionLayout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center_horizontal|top"
android:orientation="vertical"
android:weightSum="1"
android:gravity="top"
android:layout_marginTop="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#00FF00"
android:clickable="true">
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="Detailed Description:"
android:textColor="#000000"
android:id="#+id/tv_title" />
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="THIS IS THE FULL DESCRIPTION"
android:textColor="#000000"
android:id="#+id/tv_fullDescription" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
Logcat:
08-07 11:20:47.799 13896-13896/com.lorentzos.swipecards.example I/System.out: DEBUG: job desc fragment loaded!
08-07 11:20:47.855 13896-13941/com.lorentzos.swipecards.example W/EGL_emulation: eglSurfaceAttrib not implemented
08-07 11:20:47.855 13896-13941/com.lorentzos.swipecards.example W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xaaa7f880, error=EGL_SUCCESS
08-07 11:20:48.002 13896-13941/com.lorentzos.swipecards.example V/RenderScript: 0xa1408000 Launching thread(s), CPUs 2
08-07 11:20:49.798 13896-13941/com.lorentzos.swipecards.example E/Surface: getSlotFromBufferLocked: unknown buffer: 0xae433ca0
When I use the bottom bar (not working- no fragment opened but toast displayed):
When I don't use the bottom bar (workin-fragment opened, background is green):
try to link a pic of problem and without problem(no bottombar) and since you are using merge the layout hierarchy will be laid off according to your activity's viewgroup(linear,relative) constraints(we don't know what they are like).
as you said when there is no bottombar ,you fragment displays perfectly though when the bottombar it there ,problem stats ,as per your log in fragment indicating that your fragment is loading perfectly even though when bottombar is visible mean fragment is there but is not visible ,seems like your fragment didn't get the appropriate space to get displayed.
other solution can be adding bottom bar to your fragment instead of activity to avoid any overlapping ,like
mBottomBar.attach(findViewById(R.id.fragmentContainer), savedInstanceState);
OK, I think the solution for this should be simple, from what I can see in your code, you are attaching the BottomBar to your activity, I think this is the problem. If you were to read the readme.md in the roughike/BottomBar github page you'd find this
Why is it overlapping my Navigation Drawer?
All you need to do is instead of attaching the BottomBar to your Activity, attach it to the view that has your content. For example, if your fragments are in a ViewGroup that has the id fragmentContainer, you would do something like this:
mBottomBar.attach(findViewById(R.id.fragmentContainer), savedInstanceState);
So, since navigation drawer works with transition a fragment in and out of view with animation, the same thing is happening when you are adding a new fragment to your activity.
The Solution
From what I can see in your code, your fragment container id is this: fragment_container in your activity layout. So according to the documentation you'd just need to do attach your bottomBar to the fragment_container instead of MainActivity.this
mBottomBar.attach(findViewById(R.id.fragment_container), savedInstanceState);
If the above doesn't work try this
What you'd need to do is add an extra FrameLayout to hold your bottombar, which has a transparent background, but is on top of your fragment.
So change your main_activity layout to
<merge
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">
<com.lorentzos.flingswipe.SwipeFlingAdapterView
android:id="#+id/frame"
android:background="#color/laborswipe_lightgray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:layout_gravity="top" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/holder_bottombar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"/>
</merge>
Now in the code instead of attaching the bottom bar to mainactivity, just attach it to the holder like so
mBottomBar.attach(findViewById(R.id.holder_bottombar), savedInstanceState);
I have a numberpicker dialog to select hours and minutes (15 minutes interval). It works fine on large screens but on small screens and older devices it gets stuck if I try to scroll using touch.
I am extending the android.widget.NumberPicker and reducing the font size which helps on some devices.
public class TimerNumberPicker extends android.widget.NumberPicker {
public TimerNumberPicker(Context context) {
super(context);
}
public TimerNumberPicker(Context context, AttributeSet attrs){
super(context,attrs);
}
#Override
public void addView(View child) {
super.addView(child);
updateView(child);
}
#Override
public void addView(View child, ViewGroup.LayoutParams params) {
super.addView(child, params);
updateView(child);
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
updateView(child);
}
private void updateView(View view) {
if(view instanceof EditText){
((EditText) view).setTextSize(TypedValue.COMPLEX_UNIT_SP,16);
view.setTag(FontUtil.TAG_CONDENSED_LIGHT);
FontUtil.getInstence(getContext()).setFontByTag(view);
}
}
}
What makes it more complicated is that the minutes have only values (0, 15, 30, 45) and I have to setWrapSelectorWheel to false?
I am thinking if it will be possible to always show the up and down arrows and then disabling scroll.
Change it to an Edittext with the type of number, and use a Toast if the client put a number upper or below of your range. It's more elegant visually, and that will not give you any problem at all
you can use Fantastic PickView library
See PickView
This is a helper lib for us to pick date or province like IOS system WheelView widget.
Maybe you could solve it using a TimePicker.
This could be the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:padding="8dp"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TimePicker
android:id="#+id/time_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="#+id/date_time_set"
android:layout_width="match_parent"
android:text="Set"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
While the dialog could be included in a normal alert:
private void showTimePicker(){
final View dialogView = View.inflate(getActivity(), <time_picker_container_layout_id>, null);
final AlertDialog alertDialog = new AlertDialog.Builder(getActivity()).create();
dialogView.findViewById(R.id.date_time_set).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TimePicker timePicker = (TimePicker) dialogView.findViewById(R.id.time_picker);
String time = timePicker.getCurrentHour()+":"+timePicker.getCurrentMinute());
dateTime.setText(time);
alertDialog.dismiss();
}});
TimePicker timePicker = (TimePicker) dialogView.findViewById(R.id.time_picker);
timePicker.setCurrentHour(<default_hour>);
timePicker.setCurrentMinute(<default_minute>);
alertDialog.setView(dialogView);
alertDialog.show();
}
If you need to show just the 15', you can round the picked minutes to the closest valid value.
The scrollView in the layout will reduce problems related the picker size.
I hope it helped.
I'm looking for a way to implement a dialog which asks for confirmation when clicking on the delete button of my ListView row. I tried to do it inside my custom ArrayAdapter, but as it is no Activity I don't know how to do it.
When I put the whole onClick-Listener inside the MainActivity, I have no clou how to find out which position the button was clicked so that I can remove it afterwards.
public class ServiceAdapter extends ArrayAdapter<Service> {
private final Singleton singleton = Singleton.getInstance();
private ArrayList<Service> services;
public ServiceAdapter(Context context, ArrayList<Service> services) {
super(context, 0, services);
this.services = services;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Service service = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.listview_row, parent, false);
}
// Lookup view for data population
TextView quantity = (TextView) convertView
.findViewById(R.id.QUANTITY_CELL);
TextView description = (TextView) convertView
.findViewById(R.id.DESCRIPTION_CELL);
Button delete = (Button) convertView.findViewById(R.id.BUTTON_DELETE);
// Populate the data into the template view using the data object
quantity.setText(String.valueOf(service.getQuantity()));
description.setText(service.getDescription());
// Set up the listener for the delete button.
final View view = convertView;
view.setTag(Integer.valueOf(position));
delete.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
Integer index = (Integer) view.getTag();
services.remove(index.intValue());
notifyDataSetChanged();
}
});
// Return the completed view to render on screen
return convertView;
}
}
public class MainActivity extends Activity {
private ListView serviceList;
private ArrayList<Service> services;
private ServiceAdapter adapter;
private final Singleton singleton = Singleton.getInstance();
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceList = (ListView) findViewById(R.id.service_list);
adapter = new ServiceAdapter(this, services);
serviceList.setAdapter(adapter);
serviceList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Service temp = services.get(position);
singleton.setQuantity(temp.getQuantity());
singleton.setDescription(temp.getDescription());
setPosition(position);
openDetailedEntry();
}
});
}
public void openDetailedEntry() {
Intent i = new Intent(this, DetailedEntryActivity.class);
// Check if the meant Activity is actually resolvable
if (i.resolveActivity(getPackageManager()) != null)
startActivity(i);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp" >
<TableLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:paddingTop="8dp"
>
<ListView
android:id="#+id/service_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="2dp" />
</TableLayout>
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listview_row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="4dip"
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:orientation="horizontal">
<TextView android:id="#+id/QUANTITY_CELL"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="20sp"
/>
<TextView android:id="#+id/DESCRIPTION_CELL"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_weight="6" />
<Button
android:id="#+id/BUTTON_DELETE"
android:layout_width="0dp"
android:layout_height="35dp"
android:layout_weight="1"
android:textSize="12sp"
android:focusable="false"
android:text="#string/delete" />
</LinearLayout>
Let me know if you need something.
Construct AlertDialog in ServiceAdapter Like this,
private AlertDialog mDialog;
private int mListRowPosition;
public ServiceAdapter(Context context, ArrayList<Service> services) {
super(context, 0, services);
this.services = services;
//Create AlertDialog here
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("Your Message")
.setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Use mListRowPosition for clicked list row...
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object
mDialog = builder.create();
}
Create method in ServiceAdapter Like,
private void showDialog(int position)
{
mListRowPosition = position;
if(mDialog != null)
mDialog.show();
}
Now in onClick() Just call
showDialog(position); // But make position of getView() as final...
I'm trying to create a page with an edit mode, what i mean by this, is that you can click on a button and all textview transforms into edittext. So i created a custom view for this with view switcher :
custom view :
public class EditabeText extends ViewSwitcher{
private Context m_context;
private View m_view;
private TextView tv;
private EditText edit;
public EditabeText(Context context, AttributeSet attrs) {
super(context, attrs);
m_context = context;
lauch();
}
public EditabeText(Context context) {
super(context);
m_context = context;
lauch();
}
public void lauch(){
inflate();
bind();
}
public void inflate(){
LayoutInflater inflater = (LayoutInflater)m_context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
m_view = inflater.inflate(R.layout.editable_text_view, this);
}
public void bind(){
tv = (TextView) m_view.findViewById(R.id.tv_editable_text);
edit = (EditText) m_view.findViewById(R.id.edit_editable_text);
}
public void init(){
}
public void showEditText(){
if(this.getCurrentView() == tv){
this.showNext();
Toast.makeText(m_context, "showing ... edit mode", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(m_context, "already in tv mode", Toast.LENGTH_SHORT).show();
}
}
public void showTextView(){
if(this.getCurrentView() == edit){
this.showPrevious();
Toast.makeText(m_context, "showing ... tv mode", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(m_context, "already in edit mode", Toast.LENGTH_SHORT).show();
}
}
}
And here's the xml :
<ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/tv_editable_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Text" />
<EditText
android:id="#+id/edit_editable_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:hint="Enter text" />
</ViewSwitcher>
And here's my main activity :
public class Test2 extends Activity {
EditabeText edit_tv;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
LinearLayout ll_layout = new LinearLayout(getApplicationContext());
//
edit_tv = new EditabeText(getApplicationContext());
//
Button button = new Button(getApplicationContext());
button.setText("change");
button.setOnClickListener(listener);
//
ll_layout.addView(edit_tv);
ll_layout.addView(button);
//
setContentView(ll_layout);
}
public OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "click", Toast.LENGTH_SHORT).show();
edit_tv.showEditText();
}
};
}
But it says "already in tv mode" while the TextView is the first view inside the view switcher.
Don't use in the layout file(R.layout.editable_text_view is what you posted I guess) another ViewSwitcher, because this will make you have a ViewSwitcher(EditabeText class) which has a single child view, another ViewSwitcher which has two child views(a TextView and an EditText). In this case, showing the TextView or the EditText will always fail because this.getCurrentView() == tv(or == edit) will match the wrapper ViewSwitcher to the TextView or EditText. Change your layout like this:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/tv_editable_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Text" />
<EditText
android:id="#+id/edit_editable_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:hint="Enter text" />
</merge>