custom TextView is not showing text but shows only background - java

I have an strange behavior of TextView it beats me how can I figure out a solution to this issue.
I have a layout
<?xml version='1.0' encoding='utf-8' ?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="horizontal"
android:visibility="visible">
<View
android:id="#+id/them_avatar_spacer"
android:layout_width="#dimen/avatar_size"
android:layout_height="0.0dip"
android:visibility="gone"/>
<org.slabs.fc.chatstarter.ui.CircularImageView
android:id="#+id/them_avatar"
android:layout_width="#dimen/avatar_size"
android:layout_height="#dimen/avatar_size"
android:layout_marginLeft="5dp"
android:layout_marginTop="10dp"
android:src="#drawable/default_avatar"
android:visibility="gone"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4.0dip"
android:gravity="start"
android:orientation="vertical">
<TextView
android:id="#+id/chat_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2.0dip"
android:layout_marginLeft="6.0dip"
android:layout_marginTop="6dp"
android:includeFontPadding="false"
android:text="Title"
android:textColor="#color/black_light"
android:textSize="#dimen/chat_name_fontsize"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="horizontal">
<TextView
android:id="#+id/chat_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="63.0dip"
android:autoLink="all"
android:background="#drawable/selectable_balloon_left"
android:clickable="true"
android:lineSpacingExtra="#dimen/text_body_line_spacing"
android:linksClickable="true"
android:text="Every message text comes here"
android:textColor="#color/black_heavy"
android:textIsSelectable="false"
android:textSize="#dimen/chat_text_msg_fontsize"
android:visibility="visible"/>
<TextView
android:id="#+id/chat_time"
android:layout_width="60.0dip"
android:layout_height="wrap_content"
android:layout_gravity="start|center"
android:layout_marginLeft="-60.0dip"
android:text="12:15"
android:textColor="#color/black_lightest"
android:textSize="#dimen/chat_timestamp_fontsize"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
It gives me desired layout in a Design option of XML editor, as shown
But when I run the App the TextView with id chat_text is not showing and I get a result like below,
Then I tried to create a custom TextView I simply created one as
public class ChatThemTextView extends TextView {
public ChatThemTextView(Context context) {
super(context);
}
public ChatThemTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChatThemTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
ViewGroup.MarginLayoutParams margins = ViewGroup.MarginLayoutParams.class.cast(getLayoutParams());
int margin = 5;
margins.topMargin = margin;
margins.bottomMargin = margin;
margins.leftMargin = margin;
margins.rightMargin = margin;
setLayoutParams(margins);
}
}
At this point other issue came out which is when I add the first message I don't get anything but when I add the second message I get the background but TextView is still not showing as shown here
You all see what is expected how the output comes, any help is appreciated...
Update
ViewHolder is just simple
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
adapter class goes here
public class ChatMessageAdapter extends RecyclerView.Adapter<ViewHolder> {
private List<ChatMessage> mMessageList;
private Context mContext;
private boolean isMe = false;
public ChatMessageAdapter(Context mContext, List<ChatMessage> mMessageList){
this.mContext = mContext;
this.mMessageList = mMessageList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = View.inflate(mContext, R.layout.chat_them_container, null);
return new ThemMessageHolder(rootView);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
ThemMessageHolder messageHolder = (ThemMessageHolder) holder;
messageHolder.mMessageTextView.setText(mMessageList.get(position).getMessage());
messageHolder.mSentAtTextView.setText(mMessageList.get(position).getSentAt());
Log.e("MSG_TEXT", mMessageList.get(position).getMessage());
}
#Override
public int getItemCount() {
return mMessageList.size();
}
public void clearData() {
mMessageList.clear(); //clear list
this.notifyDataSetChanged();
}
}
important
I get the Sting value at
Log.e("MSG_TEXT", mMessageList.get(position).getMessage());
but TextView is not showing that String...
.gitignore is
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
and settings.gradle is just
include ':app'

You may change:
View rootView = View.inflate(mContext, R.layout.chat_them_container, null);
To:
View rootView = LayoutInflater.from(mContext).inflate(R.layout.chat_them_container, parent, false);
It will respect the LayoutParams of its parent, and hence fix your issue.

Related

Making the height of the ViewPager equal to the highest item in the PagerAdapter

I have a ViewPager and use it to swipe between views not Fragments .
And when I give the View Pager wrap_content height , it doesn't show anything . So I had to give it a fixed height . But I had another problem , when the item's height is larger than the fixed one , the view doesn't be shown correctly (And I use TextView as the View) . So what should I do to make the height of the ViewPager is equal to the highest one .
I use PagerAdapter like the code below .
public class IndicatorViewPagerAdapter extends PagerAdapter {
private Context context;
public int[] images = {R.drawable.userprofile,R.drawable.userprofile,R.drawable.userprofile,R.drawable.userprofile,R.drawable.userprofile};
public int[] texts = {R.string.first_text,R.string.second_text,R.string.third_text,R.string.fourth_text,R.string.fifth_text};
LayoutInflater layoutInflater;
Typeface typeface;
String lang;
public IndicatorViewPagerAdapter(Context context,String lang) {
this.context = context;
this.lang=lang;
}
#Override
public int getCount() {
return texts.length;
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view==object;
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.feeds_item_layout,null);
ImageView imageView = view.findViewById(R.id.image);
TextView textView = view.findViewById(R.id.desc);
imageView.setImageResource(images[position]);
String text = context.getResources().getString(texts[position]);
Log.e("text"," "+text);
textView.setText(text);
//ViewPager viewPager = (ViewPager) container;
if(lang.equals("en")) {
typeface = ResourcesCompat.getFont(context, R.font.knowledge_regular);
}
else {
typeface = ResourcesCompat.getFont(context, R.font.thesans_plain);
}
textView.setTypeface(typeface);
container.addView(view);
return view;
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
ViewPager viewPager = (ViewPager) container;
View view = (View) object;
viewPager.removeView(view);
}
}
and Here I use the ViewPager in XML
<LinearLayout
android:layout_width="match_parent"
android:layout_height="#dimen/_150sdp"
android:layout_marginTop="10dp"
android:background="#85d7d5d6"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/leftArrowTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/arrow_color"
android:textSize="#dimen/_20ssp" />
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="#+id/rightArrowTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/arrow_color"
android:textSize="#dimen/_20ssp" />
</LinearLayout>
I don't quite understand why you posted your IndicatorViewPagerAdapter code.
Your adapter code is used to populate data, it has nothing to do with the size of the view, which is the ViewPager.
To me it probably makes more sense to try and customize the viewpager and apply manual resize logic. So I looked for this on stackoverflow, as found a popular stackoverflow post where you even commented on, but it seems you were also confused on how to actually implement it. To be clear, I haven't tested this at all but it seems you were confused by the difference between adapters and views.
public class WrapContentHeightViewPager extends ViewPager {
public WrapContentHeightViewPager(Context context) {
super(context);
}
public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Then change your XML to
<TextView
android:id="#+id/leftArrowTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/arrow_color"
android:textSize="#dimen/_20ssp" />
<my.package.name.WrapContentHeightViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" />
<TextView
android:id="#+id/rightArrowTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/arrow_color"
android:textSize="#dimen/_20ssp" />
</LinearLayout>
I tried your code and it's working with a slight change. You just need to check your ViewPager item's design. ViewPager displays even with wrap_content height. Look at the below code:
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
View view = LayoutInflater.from(context).inflate(R.layout.feeds_item_layout,container, false);
...
container.addView(view);
return view;
}

How to limit the height of a RecyclerView inside a NestedScrollView

Within my current Android application, I have a screen that displays an android.support.v4.app.DialogFragment.
This DialogFragment view contains the following UI components
HEADING
== Sub Heading
== NestedScrollView
==== RecyclerView
==== RadioGroup
==== Spinner
==== EditText
==== Action Buttons
The DialogFragment is configured to be Full Screen using Style as follows:-
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppDialogTheme);
}
My dialog style is
<!-- Define your custom dialog theme here extending from base -->
<style name="AppDialogTheme" parent="Theme.AppCompat.Light.Dialog">
<!-- Define color properties as desired -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#000</item>
<item name="android:textColorHighlight">#color/background_url</item>
<item name="colorAccent">#color/dark_grey</item>
<item name="colorControlNormal">#color/colorPrimaryDark</item>
<!-- Define window properties as desired -->
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">#android:color/white</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
The reason I employ a NestedScrollView is so that the View will work in both Portrait and Landscape mode.
I wish to limit the height of the RecyclerView
The closest I have got is using the layout below.
<?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="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:padding="10dp"
android:text="Heading"
android:textSize="20sp"
android:textStyle="bold" />
<View
android:id="#+id/divider"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_marginTop="5dp"
android:background="#c0c0c0" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="5"
android:orientation="vertical">
<TextView
android:id="#+id/sub_headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
android:textSize="16sp"
android:textStyle="normal" />
<android.support.v7.widget.RecyclerView
android:id="#+id/dummy_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_marginStart="9dp"
android:layout_marginEnd="9dp"
android:layout_weight="1"
android:background="#drawable/rv_border"
android:fadingEdge="horizontal"
android:fadingEdgeLength="10dp"
android:padding="10dp"
android:requiresFadingEdge="vertical" />
<RadioGroup
android:id="#+id/myRadioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:checkedButton="#+id/sound">
<RadioButton
android:id="#+id/sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sound" />
<RadioButton
android:id="#+id/vibration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration" />
<RadioButton
android:id="#+id/silent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Silent" />
</RadioGroup>
<EditText
android:id="#+id/notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Notes" />
<LinearLayout
android:id="#+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Cancel" />
<TextView
android:id="#+id/submit_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Submit" />
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
By using weightSum on the inner LinearLayout of the NestedScrollView I can limit the height of the Recyclerview. However the NestedScrollView height is far too large, with more than half its height being blank.
How can I limit the height of my RecyclerView and get NestedScrollView to wrap_content?
I've tried NestedScrollView with height wrap_content but this has no effect.
How can I achieve the desired UI? Thanks in advance!
Instead of having a NestedRecyclerView, I would like to suggest to have a header and a footer added to your RecyclerView which will nicely place the overall content as far as I have seen your layout. I want to provide you a link to my answer here where you can find how to add a footer and a header along with your RecyclerView.
Hence, I would like to suggest to create a view with headline_literal and the divider and use this as a header whereas the RadioGroup, EditText and the Button will be in the footer. Let me know if you face any problem with it.
I have tried to implement the behavior that you want by myself and let me know if the following implementation works for you. I have added this in Github as well.
Let us first declare an adapter for adding a header and a footer to the RecyclerView.
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class RecyclerViewWithHeaderFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int FOOTER_VIEW = 1;
private static final int HEADER_VIEW = 2;
private ArrayList<String> data; // Take any list that matches your requirement.
private Context context;
// Define a constructor
public RecyclerViewWithHeaderFooterAdapter(Context context, ArrayList<String> data) {
this.context = context;
this.data = data;
}
// Define a ViewHolder for Header view
public class HeaderViewHolder extends ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Define a ViewHolder for Footer view
public class FooterViewHolder extends ViewHolder {
public FooterViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Now define the ViewHolder for Normal list item
public class NormalViewHolder extends ViewHolder {
public NormalViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want on clicking the normal items
}
});
}
}
// And now in onCreateViewHolder, you have to pass the correct view
// while populating the list item.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(v);
return vh;
} else if (viewType == HEADER_VIEW) {
v = LayoutInflater.from(context).inflate(R.layout.list_item_header, parent, false);
HeaderViewHolder vh = new HeaderViewHolder(v);
return vh;
}
// Otherwise populate normal views
v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);
NormalViewHolder vh = new NormalViewHolder(v);
return vh;
}
// Now bind the ViewHolder in onBindViewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof NormalViewHolder) {
NormalViewHolder vh = (NormalViewHolder) holder;
vh.bindView(position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
} else if (holder instanceof HeaderViewHolder) {
HeaderViewHolder vh = (HeaderViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
#Override
public int getItemCount() {
if (data == null) {
return 0;
}
if (data.size() == 0) {
// Return 1 here to show nothing
return 1;
}
// Add extra view to show the header view
// Add another extra view to show the footer view
// So there are two extra views need to be populated
return data.size() + 2;
}
// Now define getItemViewType of your own.
#Override
public int getItemViewType(int position) {
if (position == 0) {
// This is where we'll add the header.
return HEADER_VIEW;
} else if (position == data.size() + 1) {
// This is where we'll add a footer.
return FOOTER_VIEW;
}
return super.getItemViewType(position);
}
// So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// Define elements of a row here
public ViewHolder(View itemView) {
super(itemView);
// Find view by ID and initialize here
}
public void bindView(int position) {
// bindView() method to implement actions
}
}
}
Now let us define the layouts one by one. Here is the list_item_normal.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/normal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="This is a text to be displayed in each item in the RecyclerView"
android:textSize="16sp"
android:textStyle="normal" />
</LinearLayout>
And the list_item_footer.xml should look like the following.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical">
<RadioGroup
android:id="#+id/myRadioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:checkedButton="#+id/sound">
<RadioButton
android:id="#+id/sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sound" />
<RadioButton
android:id="#+id/vibration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration" />
<RadioButton
android:id="#+id/silent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Silent" />
</RadioGroup>
<EditText
android:id="#+id/notes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Notes" />
<LinearLayout
android:id="#+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Cancel" />
<TextView
android:id="#+id/submit_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Submit" />
</LinearLayout>
</LinearLayout>
Finally, the list_item_header.xml should have the following.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/sub_headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
android:textSize="16sp"
android:textStyle="normal" />
</LinearLayout>
Now you have divided the components of your original layout into parts. Hence the main layout should look like the following.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/headline_literal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:padding="10dp"
android:text="Heading"
android:textSize="20sp"
android:textStyle="bold" />
<View
android:id="#+id/divider"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_marginTop="5dp"
android:background="#c0c0c0" />
<android.support.v7.widget.RecyclerView
android:id="#+id/dummy_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp" />
</LinearLayout>
Hence, I am sharing one sample Activity to run this code which will show the overall implementation.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private ArrayList<String> data = new ArrayList<String>();
private RecyclerViewWithHeaderFooterAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeData();
initializeRecyclerView();
}
private void initializeRecyclerView() {
mRecyclerView = findViewById(R.id.dummy_rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new RecyclerViewWithHeaderFooterAdapter(this, data);
mRecyclerView.setAdapter(adapter);
}
private void initializeData() {
for (int i = 0; i < 10; i++) data.add("Position :" + i);
}
}
Hope that helps!
Customize Recycler view to set maxHeight.
public class MaxHeightRecyclerView extends RecyclerView {
private int mMaxHeight;
public MaxHeightRecyclerView(Context context) {
super(context);
}
public MaxHeightRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public MaxHeightRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightScrollView_maxHeight, mMaxHeight);
arr.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMaxHeight > 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
n attrs.xml
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
set RecyclerView height wrap_content in xml and maxHeight to fixwidth in dp.
The RecyclerView will consume height wrap_content till fixWidth which you set, after reaching to maxHeight, the RecyclerView will scrollable.
If you also need to limit the size of your NestedScrollView by makeing a custom NestedScrollView:
public class CustomNestedScrollView extends NestedScrollView {
private int maxHeight;
private final int defaultHeight = 200;
public CustomNestedScrollView(Context context) {
super(context);
}
public CustomNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode()) {
init(context, attrs);
}
}
public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode()) {
init(context, attrs);
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
if (!isInEditMode()) {
init(context, attrs);
}
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomNestedScrollView);
//200 is a defualt value
maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.CustomNestedScrollView_maxHeight, defaultHeight);
styledAttrs.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
attr.xml
<declare-styleable name="CustomNestedScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
example layout for custom NestedScrollView:
<your.package.CustomNestedScrollView
android:layout_weight="1"
app:maxHeight="90dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!--Child view with RecyclerView here-->
</your.package.CustomNestedScrollView>
Along with this custom NestedScrollView if you apply the customization of your RecyclerView then it will work exactly how you want. I hope this helps!

What did I do wrong in my custom view to cause it to not be rendered in my expandable list view (other views render fine) - Android

I'm trying to create an expandable listview in Android that that uses a custom UI component as the childview but I'm having trouble actually getting it to render. When tracing my app with the debugger, I see that my custom UI object is instantiated and data is set but it seems like its then not added to the view even though I am returning the view at the end of getChildView().
My ultimate goal is to make a custom UI element, ServiceSelectionCheckbox, that I can drag and drop directly from the layout pallete.
I tried inflating the XML for this component directly to see if it was my XML that had an issue that was causing a silent failure but it seems to inflate fine. I also have tried creating a secondary simpler UI component with a solid background to see if I can get that to add properly which it does fine. This leads me to think that my issue is with my custom UI component, ServiceSelectionCheckbox.java but I can't seem to figure out what that issue is. My linter throws no errors and my apk compiles fine.
Any help pointing me in the right direction would be greatly appreciated.
MainActivity.java
public class MainActivity extends AppCompatActivity {
public HashMap<String, ArrayList<Pair<String, String>>> hashMapOfServices = new HashMap<>();
public ArrayList<Pair<String, String>> aList;
public ExpandableListView list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
aList = new ArrayList<Pair<String, String>>();
aList.add(new Pair<>("Men's Haircut", "50.00"));
hashMapOfServices.put("Haircut", aList);
list = (ExpandableListView) this.findViewById(R.id.list);
list.setAdapter((ExpandableListAdapter) new ServiceExpandableListAdapter(this, this.hashMapOfServices));
}
}
This is my custom adapter
ServiceExpandableListAdapter.java
public class ServiceExpandableListAdapter extends BaseExpandableListAdapter {
HashMap<String, ArrayList<Pair<String, String>>> hashmap;
Context mContext;
String[] selectedCategories;
TextView price;
ImageButton increment, decrement;
ConstraintLayout lowerLayout;
public static final ArrayList<String> POSSIBLECATEGORIES = new ArrayList<>(Collections.unmodifiableList(
Arrays.asList("Barber", "Braids", "Children", "Color", "Esthetician",
"Eyebrows", "Eyelashes", "Hair Extensions", "Haircut", "Make-Up",
"Massage", "Nails", "Natural Hair", "Straightening", "Style", "Tanning", "Twists", "Waxing", "Weaves")));
public ServiceExpandableListAdapter(Context context, HashMap<String, ArrayList<Pair<String, String>>> data) {
mContext = context;
hashmap = data;
selectedCategories = data.keySet().toArray(new String[data.size()]);
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater infalater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalater.inflate(R.layout.group_heading, null);
}
TextView categoryName = (TextView) convertView.findViewById(R.id.service_category_tv);
ImageView groupHolder = (ImageView) convertView.findViewById(R.id.groupholder);
categoryName.setText(selectedCategories[groupPosition]);
return convertView;
}
#Override
public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.child_row, parent, false);
//This one works properly
// convertView = inflater.inflate(R.layout.view, parent, false);
}
//return convertView;
String category = selectedCategories[groupPosition];
ServiceSelectionCheckbox checkbox = convertView.findViewById(R.id.service_selection_checkbox);
String service = hashmap.get(category).get(childPosition).first;
checkbox.setText(service);
// Pair of format <ServiceName, Price>
Pair<String, String> testPair = new Pair<>(hashmap.get(selectedCategories[groupPosition]).get(childPosition).first, hashmap.get(selectedCategories[groupPosition]).get(childPosition).second);
if (hashmap.containsValue(testPair)) {
checkbox.setChecked(true);
} else {
checkbox.setChecked(false);
}
return convertView;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public int getGroupCount() {
return this.hashmap.keySet().size();
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return hashmap.get(selectedCategories[groupPosition]).get(childPosition);
}
#Override
public Object getGroup(int groupPosition) {
return hashmap.get(selectedCategories[groupPosition]);
}
#Override
public int getChildrenCount(int groupPosition) {
return hashmap.get(selectedCategories[groupPosition]).size();
}
}
Here is the Java file for the custom UI element.
ServiceSelectionCheckbox.java
public class ServiceSelectionCheckbox extends View implements View.OnClickListener {
private ConstraintLayout lowerLayout;
private ImageButton increment, decrement;
private float price;
private int quantity;
private TextView priceView, quantityView;
private CheckBox checkBox;
private boolean isQuantitySelectionVisible;
public ServiceSelectionCheckbox(Context context, AttributeSet attrs, String serviceName, String price, int quantity) {
super(context, attrs);
this.isQuantitySelectionVisible = true;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.service_selection_checkbox_layout, null);
priceView = (TextView) v.findViewById(R.id.service_selection_priceview);
checkBox = (CheckBox) v.findViewById(R.id.ssc_checkbox);
lowerLayout = (ConstraintLayout) v.findViewById(R.id.ssc_constraint_layout_quantity);
quantityView = (TextView) v.findViewById(R.id.ssc_service_quantity_countview);
increment = (ImageButton) v.findViewById(R.id.ssc_increase_service_count);
decrement = (ImageButton) v.findViewById(R.id.ssc_decrease_service_count);
}
public ServiceSelectionCheckbox(Context context) {
super(context);
this.init();
}
public ServiceSelectionCheckbox(Context context, AttributeSet attrs) {
super(context, attrs);
this.init();
}
private void init() {
this.isQuantitySelectionVisible = false;
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.service_selection_checkbox_layout, null);
priceView = (TextView) v.findViewById(R.id.service_selection_priceview);
checkBox = (CheckBox) v.findViewById(R.id.ssc_checkbox);
this.setOnClickListener(this);
lowerLayout = (ConstraintLayout) v.findViewById(R.id.ssc_constraint_layout_quantity);
quantityView = (TextView) v.findViewById(R.id.ssc_service_quantity_countview);
increment = (ImageButton) v.findViewById(R.id.ssc_increase_service_count);
decrement = (ImageButton) v.findViewById(R.id.ssc_decrease_service_count);
}
public ServiceSelectionCheckbox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.init();
}
#Override
public void onClick(View view) {
isQuantitySelectionVisible = !isQuantitySelectionVisible;
}
#Override
public boolean performClick() {
//set lowerlayout as visible based on visible variable
if (this.isQuantitySelectionVisible) {
lowerLayout.setVisibility(View.VISIBLE);
} else {
lowerLayout.setVisibility(View.GONE);
}
return super.performClick();
}
public void setPrice(float price) {
this.price = price;
this.priceView.setText(String.valueOf(price));
}
public void setQuantity(String quantity) {
this.quantity = Integer.parseInt(quantity);
}
public void setChecked(boolean b) {
this.checkBox.setChecked(b);
}
public void setText(String serviceName) {
this.checkBox.setText(serviceName);
}
}
And here is the corresponding XML files.
group_heading.xml
<?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:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<TextView
android:id="#+id/service_category_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="3"
android:fontFamily="sans-serif-light"
android:text="Categoy"
android:textSize="22sp"
android:background="#android:color/holo_orange_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/groupholder"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/groupholder"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:foregroundGravity="right"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/service_category_tv"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
This is the file that I believe is actually giving me problems.
child_row.xml
<?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:layout_height="wrap_content"
tools:layout_editor_absoluteY="81dp">
<com.example.gabriel.uitest.ServiceSelectionCheckbox
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/service_selection_checkbox"/>
</android.support.constraint.ConstraintLayout>
Rendered on its own, the view is created as expected.
service_selection_checkbox_layout.xml
<?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:layout_height="wrap_content"
android:visibility="visible"
tools:layout_editor_absoluteY="81dp">
<CheckBox
android:id="#+id/ssc_checkbox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:checked="false"
android:fontFamily="sans-serif-light"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="Service"
android:textAllCaps="true"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="#+id/ssc_constraint_layout_quantity"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/service_selection_priceview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif-light"
android:gravity="start|center"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
android:layout_marginRight="8dp" />
<android.support.constraint.ConstraintLayout
android:id="#+id/ssc_constraint_layout_quantity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/ssc_checkbox">
<ImageButton
android:id="#+id/ssc_decrease_service_count"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/ssc_service_quantity_countview"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/ssc_service_quantity_countview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:paddingEnd="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingStart="8dp"
android:text="Quantity"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/ssc_increase_service_count"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/ssc_decrease_service_count"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="#+id/ssc_increase_service_count"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/ssc_service_quantity_countview"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
This is the test view component that I added for test purposes mentioned earlier, this one works fine.
view.xml
<?xml version="1.0" encoding="utf-8"?>
<View
android:background="#color/colorAccent"
android:layout_width="match_parent"
android:layout_height="20dp"
xmlns:android="http://schemas.android.com/apk/res/android">
</View>
You need to define your custom view as mentioned below.Add your other codes to this view. I am keep it simple so that it will be easier for you to understand.
public class CustomView extends ConstraintLayout {
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
inflate(getContext(), R.layout. service_selection_checkbox_layout, this);
}
}
Dont nest constraint layout unless its required. In the above xml you given with one constraintlayout itself you can create the same UI.

Cells not taking desired height, when using GridLayoutManager with RecyclerView

I have created a RecyclerView (in a fragment) in which I am adding items dynamically using GridLayoutManager. In each of the cell of RecyclerView I am loading images from the server using Picasso.
Now the problem is that, when the images are loading for the first time, the RecyclerView cells take weird height as shown here:
But when I change tab and come to the home tab again, everything is the way I want, as shown here:
What I need is, to make cells take the desired width and height at the time they are getting added to the recycler view. To achieve this I have tried:
Get the width of the screen and set cells' height and width = screen size /2.
Take the width of RecyclerView and make cells' width and height equals to half of it.
I am assigning dimensions to the cell in onCreateViewHolder method of the adapter.
But in any case, I need to change tab (load fragment again) to give the cells the desired height.
I have specified the width and height of cell in its layout file as wrap content because I want them to get set according to screen size. I don't wanna hard code any value.
What am I doing wrong? Did I miss anything?
For detailed explanation, the code is given as follows:
Cell layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/category_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#android:color/darker_gray"
android:orientation="vertical"
android:weightSum="1">
<ImageView
android:id="#+id/categoryImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:layout_marginTop="0dp"
android:layout_weight="0.8"
android:src="#drawable/icon" />
<TextView
android:id="#+id/categoryName"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="0dp"
android:layout_weight="0.2"
android:text="ORAL CARE"
android:textAlignment="center"
android:textColor="#android:color/black"
android:textSize="15dp" />
</LinearLayout>
RecyclerView in fragment's layout file:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewCategories"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView11" />
RecyclerView adapter:
public class CategoryRecyclerViewAdapter extends RecyclerView.Adapter<CategoryRecyclerViewAdapter.ViewHolder> {
private List<MedicineCategoryDataModel> categoryData = new ArrayList<MedicineCategoryDataModel>();
private LayoutInflater layoutInflater;
private ItemClickListener itemClickListener;
private Context context;
private int itemWidth;
public CategoryRecyclerViewAdapter(Context context, List<MedicineCategoryDataModel> data, int width) {
this.layoutInflater = LayoutInflater.from(context);
this.categoryData = data;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.layout_category_item, parent, false);
// this is the point where I was trying to set cell's width and height.
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String categoryName = categoryData.get(position).categoryName;
holder.textViewCategoryName.setText(categoryName);
String iconUrl = categoryData.get(position).iconUrl;
EPClientLayer.getInstance().getImageHandler().loadImage(context,iconUrl, holder.imageViewCategoryIcon);
}
#Override
public int getItemCount() {
return categoryData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView textViewCategoryName;
ImageView imageViewCategoryIcon;
ViewHolder(View itemView) {
super(itemView);
textViewCategoryName = (TextView) itemView.findViewById(R.id.categoryName);
imageViewCategoryIcon = (ImageView) itemView.findViewById(R.id.categoryImage);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (itemClickListener != null) itemClickListener.onItemClick(view, getAdapterPosition());
}
}
public String getItem(int id) {
return categoryData.get(id).categoryName;
}
public void setClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}
I think you need to set the height of the ImageView of your cell item to wrap_content instead of providing the height 0dp as you are taking the weightSum. I would like to recommend to implement your cell items like this.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/category_item"
android:layout_width="match_parent"
android:layout_height="260dp"
android:background="#android:color/darker_gray">
<ImageView
android:id="#+id/categoryImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="#drawable/icon" />
<TextView
android:id="#+id/categoryName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/categoryImage"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:text="ORAL CARE"
android:textColor="#android:color/black"
android:textSize="15sp" />
</RelativeLayout>
And the RecyclerView to have the height and width set to match_parent.
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerViewCategories"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView11" />
You do not want to hard code any height, which is fine. However, I think you might consider hard-coding the optimized height of the cell item's root RelativeLayout to get the desired behaviour in all screen sizes.
make an SquareImageView as:
public class SquareImageView extends AppCompatImageView {
public SquareImageView(Context context) {
super(context);
}
public SquareImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);//replace heightMeasureSpec with widthMeasureSpec in this line
}
}
use above imageView in recyclerView Item to load image using Piccasa.
use recyclerView in xml as:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:spanCount="2"
tools:listitem="#layout/recycler_view_item"
app:layoutManager="GridLayoutManager">
</android.support.v7.widget.RecyclerView>
let me know if this solve your problem

Android: Extending CardView which content can be expanded

I extended the android CardView to a Expandable version of it, with a header title and an icon which can be rotated.
CODE
This file(view_cardview_header.xml) contains the header of the ExpandableCardView, it should be the first child and not collapsed.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textAppearance="#style/TextAppearance.AppCompat.Title" />
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
</LinearLayout>
The attrs.xml file with custom xml parameters
<resources>
<declare-styleable name="ExpandableCardView">
<attr name="expanded" format="boolean" />
<attr name="headerTitle" format="string" />
<attr name="headerIcon" format="integer" />
</declare-styleable>
</resources>
The ExpandableCardView.java class
public class ExpandableCardView extends CardView {
private static final float ROTATION_NORMAL = 0.0f;
private static final float ROTATION_ROTATED = 180f;
private static final float PIVOT_VALUE = 0.5f;
private static final long ROTATE_DURATION = 200;
private boolean isExpanded;
public ExpandableCardView(Context context) {
this(context, null);
}
public ExpandableCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ExpandableCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ExpandableCardView, 0, 0);
String titleText = a.getString(R.styleable.ExpandableCardView_headerTitle);
final Drawable drawable = a.getDrawable(R.styleable.ExpandableCardView_headerIcon);
a.recycle();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_cardview_header, this, true);
final LinearLayout parent = (LinearLayout) getChildAt(0);
final RelativeLayout header = (RelativeLayout) parent.getChildAt(0);
final TextView titleTextView = (TextView) header.getChildAt(0);
titleTextView.setText(titleText);
final ImageView toggle = (ImageView) header.getChildAt(1);
if(drawable != null) {
toggle.setImageDrawable(drawable);
}
header.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
setExpanded(toggle, !isExpanded);
onExpansionToggled(toggle);
}
});
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if(getChildAt(0) == null || getChildAt(0).equals(child)) {
super.addView(child, index, params);
} else {
((LinearLayout) getChildAt(0)).addView(child);
}
}
private void setExpanded(ImageView toggle, boolean expanded) {
isExpanded = expanded;
final LinearLayout parent = (LinearLayout) getChildAt(0);
final int childCount = parent.getChildCount();
if(expanded) {
toggle.setRotation(ROTATION_ROTATED);
for(int i = childCount - 1; i > 0; i--) {
parent.getChildAt(i).setVisibility(VISIBLE);
}
} else {
toggle.setRotation(ROTATION_NORMAL);
for(int i = 1; i < childCount; i++) {
parent.getChildAt(i).setVisibility(GONE);
}
}
}
private void onExpansionToggled(ImageView toggle) {
RotateAnimation rotateAnimation = new RotateAnimation(ROTATION_ROTATED, ROTATION_NORMAL,
RotateAnimation.RELATIVE_TO_SELF, PIVOT_VALUE, RotateAnimation.RELATIVE_TO_SELF,
PIVOT_VALUE);
rotateAnimation.setDuration(ROTATE_DURATION);
rotateAnimation.setFillAfter(true);
toggle.startAnimation(rotateAnimation);
}
}
Fragment Layout for testing the new CardView(fragment_test.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tecdroid.views.ExpandableCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/card_margin"
android:layout_marginBottom="#dimen/card_margin"
android:layout_marginLeft="#dimen/card_margin"
android:layout_marginRight="#dimen/card_margin"
app:headerTitle="TITLE"
app:headerIcon="#mipmap/ic_action_expand">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First inner child"/>
</com.tecdroid.views.ExpandableCardView>
</LinearLayout>
Result on Emulator
PROBLEM
As you can see on the image, the first inner child(TextView), is not placed under the Relative Layout(Header).
First i thought i have to override the onMeasure(int widthMeasureSpec, int heightMeasureSpec) method, but the ExpandableCardView extends indirect the ViewGroup, where the onMeasure(int widthMeasureSpec, int heightMeasureSpec) method does it all for me.
Maybe someone figure out what i have forgot, or did wrong.
UPDATE
Problem solved, see changes.
CardView extends FrameLayout, your header and first child are just unrelated child views. To have a spatial relation between children you need to use a LinearLayout or RelativeLayout with align-* parameters.
why not make a compound view with your cardview and a layout of your choice that contains the child view?

Categories