Android - Custom View - Extending EditText with Views on Top - java

I'm trying to figure out a good solution on how to extend EditText to allow me to have other Views layered on top. I am trying to make a custom View that is an EditText that has a TextView on top to display the number of characters, and an ImageView on top for a clear button.
Currently, I have it working with extending FrameLayout, but that doesn't give me the control/flexibility that I am looking for. Such as I can't use ButterKnife's #OnTextChanged as it expects a TextView, and I don't have direct access to any XML attributes of the EditText unless I pass them through.
Thanks for your time.
ClearableEditText.java
public class ClearableEditText extends FrameLayout {
public ClearableEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
protected void init() {
View inflate = inflate(getContext(), R.layout.clearable_edit_text, this);
ButterKnife.bind(this, inflate);
...
}
...
}
R.layout.clearable_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/clearable_edit"
style="#style/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
<ImageView
android:id="#+id/clearable_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:src="#drawable/button_clear"
android:visibility="invisible"
tools:visibility="visible" />
<TextView
android:id="#+id/clearable_count"
style="#style/edit_text_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
tools:text="1200" />
</FrameLayout>

I find it simpler in these situations to use an empty layout in the xml which I later fill on runtime with whatever elements I want. example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView ..... />
<Button........./>
.
.
.
<LinearLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
and in the activity
LayoutInflater inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout)findViewById(R.id.container);
MyComplexView myComplexView = new MyComplexView(inflater, container);
where MyComplexView:
public static class MyComplexView{
LinearLayout container;
LayoutInflater inflater;
TextView textView ;
ImageView img;
EditText editText;
public MyComplexView(LinearLayout container,LayoutInflater inflater ){
this.container = container;
this.inflater = inflater;
View v = (View) inflater.inflate(R.layout.mylayout, null);
container.addView(v);
textView = (TextView)v.findViewById(R.id.textview);
img = (ImageView)v.findViewById(R.id.imgaviev);
editText = (EditText)v.findViewById(R.id.edittext);
// assign whatever text change listeners or on click listeners you want
}
public void makeEditTextMultiline(boolean flag){
if(flag){
edittext.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
}
else{
edittext.setSingleLine(true);
}
}
public String getEditTextText(){
return editText.getText().toString();
}
}
After that you can create all sorts of methods in the MyComplexView class to manipulate the object.
I think it's easier this way than extending the View class.

Related

How to increase the number of text in Android programmatically

I have a page in which I'm taking the START TIME and END TIME from DATABASE.
Let's say the START TIME is 7:00 and END TIME is 22:00
I want to use this START TIME and END TIME to show in my page as textview like 7:00 8:00 9:00 and sooo on till 22:00 as textview
Also I have an imageview that will also increase when the text increases.
How can I achieve this?
Also I want the result text in Horizontal Scroll View with Imageview at top and text view as bottom of each imageview
char first = StartTime.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
int l;
for( l = StartTimeint; l<=22; l++){
Log.d("SeatsPage", "Time is "+l);
}
timeofseats.setText(Integer.toString(l));
This is I have done so far but I'm getting 23 as a result, the textview is not increasing
This is my XML File
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="#+id/llMain"
android:layout_height="match_parent"
tools:context=".SeatsPagewithDB.SeatsPage">
<ImageView
android:id="#+id/imageView11"
android:layout_width="150px"
android:layout_height="150px"
android:layout_marginStart="28dp"
android:layout_marginEnd="326dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/seat" />
<TextView
android:id="#+id/timeofseats"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="334dp"
android:background="#FF0000"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="7:00"
android:textColor="#fff"
android:textSize="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView11" />
</android.support.constraint.ConstraintLayout>
This is the result I am getting as layout
This what I want programmatically
The XML code that you write in your layout.xml file to create the UI is for static UI only. What you are asking is to create views dynamically during runtime. Although you can definitely create views using java code on a click of a button or something. But it is better to code less for the UI whenever possible and keep it separated from the program code. Instead use the tools given to us by the framework we are using.
In Android those tools include stuff like ListView, GridView and the newer and better RecyclerView. These views help you add other views dynamically to your UI in runtime. You define one of them or more (depending on your UI needs) once in your layout.xml and configure them using java code like any other view.
This is how you can use RecyclerView to achieve your goal. I can't explain everything how RecyclerView works and what each line of code does as it will make a very long post but I have tried to highlight main things briefly.
1. Add RecyclerView in your layout file.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. Create another layout file and define the template UI of the item that the RecyclerView is going to display. RecyclerView will populate each item that it holds with this layout.
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="#+id/imageView_alarm"
android:layout_width="90dp"
android:layout_height="90dp"
android:src="#drawable/alarm" />
<TextView
android:id="#+id/textView_Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="#FF0000"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:text="Time"
android:textColor="#android:color/background_light"
android:textSize="24sp" />
</LinearLayout>
3. Create a ViewHolder class that extends from RecyclerView.ViewHolder. View holder is a RecyclerView related concept. In short it works as a wrapper around the view of a single item and aids in binding new data to the view of the item. Create a bind() function inside view holder to make your life easier.
EDIT: I have updated the class by implementing the View.OnClickListener interface, modified the constructor to pass in the context from onCreateViewHolder() and adding a setItemPosition() just for the sake to pass the item position number from onBindViewHolder() all over to here so we can use this position number in our onClick() method of the interface
MyViewHolder.java [UPDATED]
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView textView;
private int itemPosition;
private Context mContext;
public MyViewHolder(#NonNull View itemView, Context context) {
super(itemView);
itemView.setOnClickListener(this);
mContext = context;
textView = itemView.findViewById(R.id.textView_Time);
}
void bind(String timeText)
{
textView.setText(timeText);
}
void setItemPosition(int position)
{
itemPosition = position;
}
#Override
public void onClick(View v) {
Toast.makeText(mContext, "You clicked item number: " + itemPosition , Toast.LENGTH_SHORT).show();
}
}
4. Create an Adapter class that extends from RecyclerView.Adapter. Adapter works as a bridge between the UI data and RecyclerView itself. An Adapter tells the RecyclerView what layout file to inflate and how many to inflate. RecyclerView job is to deal with how to inflate it on the UI.
EDIT : Just changed myViewHolder in onCreateViewHolder() to match the modified constructor of MyViewHolder. Added the call to setItemPosition() in the onBindViewHolder().
MyAdapter.java [UPDATED]
public class MyAdapter extends RecyclerView.Adapter {
List<String> timeIntervalList = new ArrayList<>();
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view , parent.getContext());
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
MyViewHolder viewHolder = (MyViewHolder) holder;
viewHolder.setItemPosition(position);
viewHolder.bind(timeIntervalList.get(position));
}
#Override
public int getItemCount() {
return timeIntervalList.size();
}
public void addItem (String timeText)
{
timeIntervalList.add(timeText);
notifyItemInserted(getItemCount());
}
}
In this adapter you will see two functions. OnCreateViewHolder() inflates the view using the template layout file for a single item and OnBindViewHolder() binds new data to the default values of the of the view just created. The data used for binding is stored in a list inside this Adapter called the timeIntervalList. This list will hold your time interval strings so they can be updated on the view.
5. Finally, use this RecyclerView where you want to use it. Like in your MainActivity.java. RecyclerView needs to be told in what fashion to display the items (e.g list , grid etc ) using a LayoutManager. LinearLayoutManager will display items either vertically or horizontally. You can see I am using your logic to increment time from string and adding new views to RecyclerView using the addItem() function of the MyAdapter class.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView myRecyclerView;
private MyAdapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myRecyclerView = findViewById(R.id.recyclerView);
myAdapter = new MyAdapter();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this , LinearLayoutManager.HORIZONTAL, false);
myRecyclerView.setLayoutManager(linearLayoutManager);
myRecyclerView.setAdapter(myAdapter);
// This is how you will populate the recycler view
String START_TIME = "7:00";
String END_TIME = "22:00";
char first = START_TIME.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
int l;
for( l = StartTimeint; l<=22; l++){
// This is where new item are added to recyclerView.
myAdapter.addItem(l + ":00");
}
}
}
This is the final result.
Change your activity layout XML code as follows,
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="#+id/llMain"
android:layout_height="match_parent"
tools:context=".SeatsPagewithDB.SeatsPage">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
...>
<LinearLayout
android:id="#+id/container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</HorizontalScrollView>
</android.support.constraint.ConstraintLayout>
Move the textview and imageview to another XML file let's call it item_view.xml (you can name it whatever you wish). we are doing so because the root view of this file will be reused.
<?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:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView11"
android:layout_width="150px"
android:layout_height="150px"
app:srcCompat="#drawable/seat"/>
<TextView
android:id="#+id/timeofseats"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FF0000"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="7:00"
android:textColor="#fff"
android:textSize="20dp"/>
</LinearLayout>
Now make following changes in your Java file
LinearLayout container = findViewById(R.id.container); // or rootView.findViewById() for custom View and Fragment
char first = StartTime.charAt(0);
int StartTimeint = Integer.parseInt(String.valueOf(first));
for(int l = StartTimeint; l<=22; l++){
Log.d("SeatsPage", "Time is "+l);
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.item_view, null);
TextView timeofseats = view.findViewById(R.id.timeofseats);
timeofseats.setText(Integer.toString(l));
container.addView(view);
}

getView() never called on custom adapter

I am aware that other people have asked this question, but I have looked at other solutions and still can't get it to work.
Adapter code:
private class CustomTextAndImageAdapter extends ArrayAdapter<String> {
private Context context;
private Activity activity;
private ArrayList<String> timeArrayList;
private ArrayList<Bitmap> weatherIconArrayList;
private ArrayList<String> descriptionArrayList;
private ArrayList<String> tempArrayList;
private ArrayList<String> popArrayList;
private ArrayList<String> windSpeedArrayList;
public final void setTimeArrayList(ArrayList<String> timeArrayList)
{
this.timeArrayList = timeArrayList;
}
public final void setDescriptionArrayList(ArrayList<String> descriptionArrayList)
{
this.descriptionArrayList = descriptionArrayList;
}
public final void setTempArrayList(ArrayList<String> tempArrayList)
{
this.tempArrayList = tempArrayList;
}
public final void setPopArrayList(ArrayList<String> popArrayList)
{
this.popArrayList = popArrayList;
}
public final void setWindSpeedArrayList(ArrayList<String> windSpeedArrayList)
{
this.windSpeedArrayList = windSpeedArrayList;
}
public final void setWeatherIconArrayList(ArrayList<Bitmap> weatherIconArrayList)
{
this.weatherIconArrayList = weatherIconArrayList;
}
public CustomTextAndImageAdapter(Context context, Activity activity, int resource)
{
super(context, resource);
this.context = context;
this.activity = activity;
}
#Override
public View getView(int position, View view, ViewGroup parent)
{
Log.d(Constants.LOG_TAG, "getView() method called");
LayoutInflater inflater = activity.getLayoutInflater();
View rowView= inflater.inflate(R.layout.itemlistrow, null, false);
TextView timeTextView = (TextView)rowView.findViewById(R.id.time);
timeTextView.setText(timeArrayList.get(position));
Log.d(Constants.LOG_TAG, "Time text view text = " + timeArrayList.get(position));
ImageView iconImageView = (ImageView) rowView.findViewById(R.id.weatherIcon);
iconImageView.setImageBitmap(weatherIconArrayList.get(position));
TextView descriptionTextView = (TextView)rowView.findViewById(R.id.description);
descriptionTextView.setText(descriptionArrayList.get(position));
TextView tempTextView = (TextView)rowView.findViewById(R.id.temp);
tempTextView.setText(tempArrayList.get(position));
TextView popTextView = (TextView)rowView.findViewById(R.id.pop);
popTextView.setText(popArrayList.get(position));
TextView windSpeedTextView = (TextView)rowView.findViewById(R.id.windSpeed);
windSpeedTextView.setText(windSpeedArrayList.get(position));
return rowView;
}
}
List item layout (itemlistrow.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/time" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/weatherIcon"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sunny"
android:id="#+id/description"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="11 C"
android:id="#+id/temp"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text = "Rain:"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text = "Wind:"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id = "#+id/pop"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text = "#+id/windSpeed"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
In some of the other solutions, it mentions overriding getCount(). Is this what I am doing wrong? If so, how would I know what to put in for getCount(), as there are multiple different ArrayLists used. Is it a case of picking one of them, as they are all the same length, e.g. timeArrayList.size()?
Using multiple ArrayList objects like that kind of defeats the purpose of using an ArrayAdapter, whose idea is to have a single source of items. Not to mention that the code right now doesn't look nice at all.
I'd suggest to first create a Weather object that will hold your data:
public class Weather {
private String time;
private Bitmap weatherIcon;
private String description;
private String temp;
private String pop;
private String windSpeed;
// build object here, provide getters, etc....
.....
}
Than your adapter can be transformed to something simpler like this:
private class CustomTextAndImageAdapter extends ArrayAdapter<Weather>
{
private LayoutInflater inflater;
public CustomTextAndImageAdapter(Context context, Activity activity, int resource, List<Weather> items)
{
super(context, resource, items);
this.inflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View view, ViewGroup parent)
{
View rowView= inflater.inflate(R.layout.itemlistrow, null, false);
TextView timeTextView = (TextView)rowView.findViewById(R.id.time);
timeTextView.setText(getItem(position).getTime());
ImageView iconImageView = (ImageView) rowView.findViewById(R.id.weatherIcon);
iconImageView.setImageBitmap(getItem(position).getWeatherIcon());
........
return rowView;
}
}
Main difference is that it's now an ArrayAdapter<Weather> and that you're passing the arguments directly in the constructor of the adapter. Users of the adapter now have to call just 1 constructor, instead of all the final methods that had to be called before.
The other major difference is that you're passing the items list to the super class. Now your adapter knows it's size (internally getCount() will be == items.size()) so getView() will be called appropriately.
As a final thought - the adapter is still not using the ViewHolder pattern, which you should totally implement! There's been numerous posts for it, so just search a bit and you'll find it.
This is not a good way to populate a ListView using an adapter which populates the data from multiple ArrayList. Generally we use a single source of dataset to be passed to an adapter in case of showing a list in Android.
So in your case, when you'll call the notifyDatasetChanged it shouldn't take effect in the list properly as far as I can guess.
notifyDatasetChanged basically calls the getCount function of the adapter and checks if the size of the ArrayList associated with the adapter is changed or not. If the size of the ArrayList is changed, it refreshes the ListView and the getView function gets called.
In your case, I don't see any getCount function though. getCount usually returns the size of the ArrayList associated with the adapter.
So I would suggest, using a single ArrayList to be passed to the adapter. You can merge multiple ArrayList and can use one joined HashMap in your case too. Its your decision, exactly how you can pass a single list of your dataset to the adapter to populate them into a ListView.

Using Picasso in GridView: ImageView is there but with no image

I'm trying to implement Picasso to help load images into a GridView. Currently, the code below runs without any problems, but the GridView item is just blank. I know the particular ImageView is actually there, because if I select it and hold down, the selected_state drawable is activated, as you can see in this screenshot.
Can you please help me find the problem? Thanks!
GalleryFragment.java:
//in onCreateView()
v = inflater.inflate(R.layout.fragment_gallery, parent, false);
GridView gridView = (GridView) v.findViewById(R.id.fragmentGalleryGridview);
gridView.setAdapter(new GalleryAdapter(getActivity()));
gridView.setOnScrollListener(new GalleryScrollListener(getActivity()));
return v;
GalleryAdapter.java extends BaseAdapter:
public GalleryAdapter(Context mContext)
{
this.mContext = mContext;
inflater = LayoutInflater.from(mContext);
//get ArrayList<String> mPaths from SQLite database
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
SquareImageView view = (SquareImageView) convertView;
if (view == null)
{
view = new SquareImageView(mContext);
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
Picasso.with(mContext)
.load(mPaths.get(position))
.fit()
.into(view);
return view;
}
SquareImageView.java:
public class SquareImageView extends ImageView
{
//three constructors
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
}
fragment_gallery.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eeeeee"
android:layout_marginTop="?android:attr/actionBarSize">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentGalleryGridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:columnWidth="100dp"
android:numColumns="auto_fit"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:drawSelectorOnTop="true"
android:stretchMode="columnWidth"/>
</FrameLayout>
This is how I've successfully implemented the Picasso library with a GridLayout...GridView requires you to use a ListAdapter for the items and I had the same problem but solved it using a GridLayout instead:
public class ImageHandler {
private static Picasso instance;
public static Picasso getSharedInstance(Context context)
{
if(instance == null)
{
instance = new Picasso.Builder(context).executor(Executors.newSingleThreadExecutor()).memoryCache(Cache.NONE).indicatorsEnabled(true).build();
return instance;
}
else
{
return instance;
}
}
}
After I implement the class here is how I access in code and load the image into my ImageView which is part of a GridLayout.
// imString = "http://filepath.file.jpg" as example
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
GridLayout grid = (GridLayout) findViewById(R.id.viewAllGrid);
grid.canScrollVertically(1);
ImageButton image;
for(int i = 0; i < itemsList.size(); i++){
aContainer= (LinearLayout) inflater.inflate(R.layout.layout_container, null);
image = (ImageButton) aContainer.findViewById(R.id.imageViewOrButton);
ImageHandler.getSharedInstance(getApplicationContext()).load(imString).skipMemoryCache().fit().into(image);
grid.addView(aContainer);
}
Here is the layout_container
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:id="#+id/Container"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageButton
android:layout_width="100dp"
android:layout_height="130dp"
android:background="#null"
android:id="#+id/seriesThumbnail"/>
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold"
android:textColor="#ffffffff"
android:id="#+id/title"
android:autoText="false"
android:ellipsize="end"
android:maxLines="1" />
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/publisher"
android:text="Francis Chan"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout>
And here is the viewAllGrid layout schema
<GridLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="20dp"
android:id="#+id/viewAllGrid"
android:layout_gravity="center_horizontal"
android:columnCount="3"
android:layout_weight=".8"
android:orientation="horizontal">
</GridLayout>
I had the same problem and it was because I was missing the permissions
<!-- This is required for Picasso to work. -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- The following permissions are OPTIONAL. -->
<!-- Used to adjust the work load depending on the type of network the device is using. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Inflate a layout inside other layout's LinearLayout

I've got this Layout:
ComposeView http://img845.imageshack.us/img845/2121/d6zp.png
The 2 borders (left,right )are filled by icons. When I touch one of these icons I access to other activity. The top black bar is a custom title bar.
The clear grey inside space is where I need to fit all activities that I've got on my app. So this layout would be something like a menu layout that would be static in all the activities.
This is the Layout xml:
menu_view.xml
<RelativeLayout
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="com.example.MenuView" >
<!-- Show top black custom title bar-->
<include
layout="#layout/custom_tittlebar" >
</include>
<!-- Button columns -->
<LinearLayout
android:id="#+id/lefthandmenu"
android:layout_width="85dip"
android:layout_height="match_parent"
android:layout_below="#id/title"
android:layout_alignParentLeft="true"
android:orientation="vertical"
android:background="#drawable/border_cut" >
<ImageView
... />
...
</LinearLayout>
<LinearLayout
android:id="#+id/righthandmenu"
android:layout_width="85dip"
android:layout_height="match_parent"
android:layout_below="#id/title"
android:layout_alignParentRight="true"
android:orientation="vertical"
android:background="#drawable/border_cut" >
<ImageView
... />
...
</LinearLayout>
<!-- Blank space which will contain other activities -->
<LinearLayout
android:id="#+id/activitycontent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="#id/righthandmenu"
android:layout_toRightOf="#id/lefthandmenu"
android:layout_below="#id/title"
android:orientation="vertical" >
</LinearLayout>
And this is the class where are defined all the icon's onClickListeners.
MenuView.java
public class MenuView extends RelativeLayout {
private final LayoutInflater inflater;
Context context;
public MenuViewActivity(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = 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);
}
final OnClickListener launch_nav = new OnClickListener() {
#Override
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), Navigation.class));
}
};
Well, having this (I'm not sure if its all ok, maybe I'm doing something wrong with the inflate method or something like this), now the thing would be to define the other activitie's layouts to be inside this view. To do this, I write:
ExampleActivity.java
public class ExampleActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout inside_menu_view = (LinearLayout)findViewById(R.id.activitycontent);
View this_layout = getLayoutInflater().inflate(R.layout.main, inside_menu_view, true);
inside_menu_view.addView(this_layout);
But I'm getting a NullPointerException on this last line. So, something when inflating on this snippets must be wrong.
Hey use the following structure. put. include your common layout in each and every activity layout
<?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:orientation="vertical" >
<!-- Show top black custom title bar-->
<include
android:id="#+id/ll_top"
layout="#layout/custom_tittlebar"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
</include>
<include
android:id="#+id/ll_left"
layout="#layout/custom_left"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" >
</include>
<include
android:id="#+id/ll_right"
layout="#layout/custom_right"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" >
</include>
<Button
android:id="#+id/btn_center"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="center"
android:layout_toRightOf="#+id/ll_left"
android:layout_centerHorizontal="true"
android:layout_below="#+id/ll_top"
></Button>
</RelativeLayout>
You can have one main activity like BaseActivity that is extended by FragmentActivity. Base activity extends SlidingFragmentActivity and implements basic structure like menu etc. FragmentActivity onCreate method is :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fm = getSupportFragmentManager();
//This is main content that is switchable
mContent = new MainListActivity();
//Additionally you can define those two lines as fragments and add them as default on both sides :
sideFragments = new sideFragments(this);
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.content_frame, mContent);
ft.replace(R.id.side_framgments, sideFragments);
ft.commit();
}
When you press some of those buttons from your menu(left and right border) you will change the fragment in middle of the screen with this function in FragmentActivity :
public void switchContent(Fragment fragment, String tag) {
mContent = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame,fragment).addToBackStack(tag).commit();
}
Note R.id.content_frame is the XML that is switchable between those two lines (let say menus on both sides) and R.id.side_framgments are those two lines in between the main layout that is switchable.

Wrong values after rotation when more components with the same IDs are in one layout

I created a deletable EditText. Here is the layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" >
<EditText
android:id="#+id/cacEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:paddingRight="35dp" />
<Button
android:id="#+id/cacClearButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:background="#android:drawable/ic_input_delete"
android:visibility="invisible" />
</RelativeLayout>
The problem is that when I put more of these into one layout and rotate to landscape, they all suddenly have the same value. I suppose it is because the system restores the values by ID and each component consists of elements of the same IDs. How can I solve this problem?
More info:
deletable EditText (cz.kns.uome.component.CleanAndClearEditText) in a layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/cz.kns.uome"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<cz.kns.uome.component.CleanAndClearEditText
android:id="#+id/nameEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#+id/contactPickerButton"
app:hint="#string/person_name"
app:type="textPersonName" />
<ImageButton
android:id="#+id/contactPickerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:contentDescription="#string/select_contact"
android:src="#drawable/ic_action_dropdown" />
</RelativeLayout>
<cz.kns.uome.component.CleanAndClearEditText
android:id="#+id/emailEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:hint="#string/person_email_opt"
app:type="textEmailAddress" />
<cz.kns.uome.component.CleanAndClearEditText
android:id="#+id/descriptionEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:hint="#string/description_opt"
app:type="text" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/saveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="#string/save" />
<Button
android:id="#+id/cancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="#string/cancel" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
java class
public class CleanAndClearEditText extends RelativeLayout {
private final EditText editText;
private final Button clearButton;
public CleanAndClearEditText(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.clean_and_clear_edit_text, this);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
R.styleable.ClearAndCleanEditText);
// ?????
// I have to somehow access that edittext here but static id does not work
editText = (EditText) findViewById(R.id.cacEditText);
editText.addTextChangedListener(textWatcher);
editText.setHint(typedArray.getString(R.styleable.ClearAndCleanEditText_hint));
String type = typedArray.getString(R.styleable.ClearAndCleanEditText_type);
if (type != null) {
editText.setInputType(InputTypes.get(type));
}
clearButton = (Button) findViewById(R.id.cacClearButton);
clearButton.setOnClickListener(clearButtonListener);
}
// rest ommited
}
I'm running on this issue, and I was able to "solve" this, by removing all views in the layout that contains the custom views, implement this:
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
// retain your data in the bundle then remove views
layout.removeAllViewsInLayout();
}
So you can get your data back from the bundle in the onCreate method.
See if this helps:
public class CleanAndClearEditText extends RelativeLayout {
private final EditText editText;
private final Button clearButton;
public CleanAndClearEditText(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.clean_and_clear_edit_text, this);
RelativeLayout rl = (RelativeLayout) getChildAt(0); // consider using a merge tag in R.layout.clean_and_clear_edit_text instead of the parent RelativeLayout
//clearButton = (Button) findViewById(R.id.cacClearButton);
clearButton = (Button) rl.getChildAt(1);
clearButton.setOnClickListener(null);
//editText = (EditText) findViewById(R.id.cacEditText);
editText = (EditText) rl.getChildAt(0);
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.stateToSave = editText.getText().toString();
return ss;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
editText.setText(ss.stateToSave);
}
static class SavedState extends BaseSavedState {
String stateToSave;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
stateToSave = in.readString();
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(stateToSave);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
and the modified R.layout.clean_and_clear_edit_text file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" >
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:paddingRight="35dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:background="#android:drawable/ic_input_delete"
android:visibility="invisible" />
</RelativeLayout>
Edit:
The main part of the code is from the question I posted as a comment.
OK, I see you are inflating custom code and inserting it into a linearlayout. You should use a ListView and implement your ListAdapter. You can customize your ListItem to fit your needs.
That's the way I would do it.

Categories