I'm using a custom LinearLayout (extends LinearLayout), so that and Adapter (BaseAdapter) can be attached to it.
(Thanks to the original author ofc.)
* #author Vincent Mimoun-Prat # MarvinLabs
*/
public class MyLinearLayout extends LinearLayout {
private static final String TAG = "MyLinearLayout";
private Adapter adapter;
....
private void reloadChildViews() {
removeAllViews();
if (adapter == null) return;
int count = adapter.getCount();
for (int position = 0; position < count; ++position) {
View v = adapter.getView(position, null, this);
if (v != null) {
int[] attrs =new int[]{R.attr.selectableItemBackground};
TypedArray typedArray =getContext().obtainStyledAttributes(attrs);
int backgroundResource =typedArray.getResourceId(0, 0);
v.setBackgroundResource(backgroundResource);
typedArray.recycle();
addView(v);
}
}
requestLayout();
}
}
So I'm basically adding each child dynamically via addView() method.
As you can see I've already attempted to place a ripple effect on each child view added programatically.
The child view that is added to this ViewGroup has the according attrs:
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name="contentProduct"
type="com.example.flyhigh.data.pojos_entities.content.variations.ContentProduct" />
<variable
name="touchListener"
type="android.view.View.OnTouchListener"/>
</data>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="com.example.flyhigh.ui.DailyJournalFragment"
android:clickable="true"
android:focusable="true"
setOnTouchListener="#{touchListener}"
android:background="?android:attr/selectableItemBackground"
>
<TextView
.....
/>
</LinearLayout>
</layout>
The touchListener binded is working accordingly.
The LinearLayout:
<layout
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"
tools:context="com.example.flyhigh.adapters.PhaseTypePagerAdapter2"
>
<com.example.flyhigh.custom_artifacts.MyLinearLayout
android:id="#+id/myLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical"
android:background="?selectableItemBackground" //I've tried with this on and off
android:clickable="true" //I've tried with this on and off
android:focusable="true" //I've tried with this on and off
android:divider="#drawable/divider"
android:dividerPadding="10dp"
android:showDividers="middle"
/>
</layout>
This LinearLayout is inside a ViewPager.
This ViewPager is working without FragmentManager (its Adapter is a PagerAdapter).
How do I know its the LinearLayout obfuscating the children and not the ViewPager obfuscating everything? because when playing around the LinearLayout attributes, the changes become visible, this includes the actual ripple effect of the LinearLayout itself (not its children) when touched outside a children (I've given some room for this to be possible).
In case you want to see the ViewPager:
<com.example.flyhigh.custom_artifacts.MyViewPager
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
>
I'm planning to add a Drag and Drop functionality to each children but I'm afraid that this symptom will reach that functionality also.
I'm 100% sure the solution relies on overriding a Method in the MyLayout class but which one?
I appear to have had some concepts wrong about the order of how views overlap.
It seems that the order will always be that the last nested object is the one on top of everything else (how could I've thought it otherwise...), maybe I was confused by the way the code is written (in a nested way)...
Anyways...
The problem wasn't that the parent ViewGroup was obfuscating its more inner children, BUT the other way around:
It's always the children that obfuscate its parent.
To reprise my initial question, I thought my outer layout (a custom LinearLayout) was preventing the ripple effect from its list items (a ViewGroup also).
So, the solution, without using the android:foreground= that seems to be a popular one, is the next one:
This inside the ViewGroup you want highlighted with ripple effect (my list item):
<LinearLayout
...
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:addStatesFromChildren="true" // this is the important one, but without the other attrs is useless
>
//obfuscating children should have the according:
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
Etc...etc...etc...
</LinearLayout>
Related
I'm try to create a ListView with CardView. CardView contains always 3 rows with some info, but after that it got 2n rows that looks like:
- position, name;
- image, data, image, data.
I'm using for this task object, that contains:
- object with data, that will always fill fist 3 rows;
- list of object, that i use for 2n rows.
I've tried already:
- swapping RecyclerAdapter to ArrayAdapter (helps with visibility that I change too, but not with inflating);
- creating a method that will handle all logic related to inflating that layout
- inflating inside onBindViewHolder/getView
I will paste version with inflating CardView in another method:
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
/*inflating layout and fill it with data from first object*/
View listItem = convertView;
if(listItem == null)
listItem = LayoutInflater.from(context).inflate(R.layout.card,parent,false);
//add data
//if needed to make sure that inflating will occur once. list is
//LinearLayout inside CardView, current is entire object
if(list.getChildCount() < 1)
addList(list, current);
//setting click listeners and returning view
}
private void addList(ViewGroup parent, ListItem current){
for (Item var : ListItem.getItemList()) {
View layout = LayoutInflater.from(context).inflate(R.layout.card_part, parent, false);
//setting data
ViewGroup.LayoutParams params = layout.getLayoutParams();
params.height = LinearLayout.LayoutParams.WRAP_CONTENT;
params.width = LinearLayout.LayoutParams.WRAP_CONTENT;
layout.setLayoutParams(params);
parent.addView(layout);
}
}
#EDIT: CardView
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/colorPrimary"
android:layout_margin="15dp"
app:cardCornerRadius="5dp"
app:cardElevation="25dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/id"
android:visibility="gone"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:id="#+id/name"
android:textSize="20sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:id="#+id/type"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:layout_weight="1"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_expand"
tools:ignore="ContentDescription"
android:id="#+id/show_list"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_hide"
tools:ignore="ContentDescription"
android:visibility="gone"
android:id="#+id/hide_list"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/list"
android:visibility="gone">
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Actual results:
- if i comment
if(list.getChildCount() < 1)
data fill be sometimes added few time, not only from correct object.
- now with that if layout is inflating with wrong data.
Expected result:
Inflating inside CardView add data that is correct for object and connected to it list of objects.
#EDIT2:
I've tried to just create that part of View manually instead of using LayoutInflater. That does not change anything.
After some break from this topic I found way to do it. Adapter reusing old View on getView/onBindViewHolder. If Linear Layout that conatins earlier list of others elements like TextView, ImageView etc. was not cleared before adding new elements, old will stay.
The solustion is to remove old ones. On Linear Layout I needed to call removeAllViews() before adding new elements.
I have a LinearLayout with three children in it, two RecyclerViews and an ImageView. In the code based on some condition I enable and disable these children using setVisibility().
public void onNotificationPriorityMixChanged(int mix) {
Log.d(TAG, "onNotificationPriorityMixChanged() called with: mix = [" + mix + "]");
switch (mix) {
case MIX_BOTTOM_ONLY: {
topListView.setVisibility(GONE);
bottomListView.setVisibility(VISIBLE);
spacer.setVisibility(GONE);
}
break;
case MIX_TOP_ONLY: {
topListView.setVisibility(VISIBLE);
bottomListView.setVisibility(GONE);
spacer.setVisibility(GONE);
}
break;
case MIX_NONE: {
topListView.setVisibility(GONE);
bottomListView.setVisibility(GONE);
spacer.setVisibility(GONE);
}
break;
case MIX_BOTH: {
topListView.setVisibility(VISIBLE);
bottomListView.setVisibility(VISIBLE);
spacer.setVisibility(VISIBLE);
}
break;
default:
}
}
The layout file is has a LinearLayout with a first RecyclerView (topListView) followed by the ImageView (spacer) followed by again a RecyclerView (bottomListView) in it.
The problem is that I cannot get only one child view to occupy whole parent when other two are disabled.
My question is that should I use the layout_weight attribute in XML or dynamically. and can anyone tell me what is the connection between using setVisibility() and layout_weight attribute, if there is any?
Set the layout_height of your RecyclerViews to 0dp (for Vertical LinearLayout)
I have tested this working fine
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="2"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:layout_weight="1"
android:visibility="visible"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="2dp" />
<android.support.v7.widget.RecyclerView
android:layout_weight="1"
android:visibility="visible"
android:layout_width="match_parent"
android:layout_height="0dp"/>
</LinearLayout>
for hiding any view use .setVisibility(View.GONE)
for showing any view use .setVisibility(View.VISIBLE)
I created layout file, consist of one linearlayout and two nested linearlayout inside the main one. When I using getParent method, it select the second nested linearlayout. My target was the first one nested linearlayout. So I gives the first nested linearlayout a ID called linear_top. Then I declared in the onCreateView, test and debug, I have no luck it shows that is null.
My target was AnimatedGifImageView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/linear_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/music_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Love Story"
android:textAppearance="?android:attr/textAppearanceLarge" />
<com.music.flow.lib.AnimatedGifImageView
android:id="#+id/music_anim"
android:layout_width="match_parent"
android:layout_height="76dp"
android:scaleType="fitXY"
android:contentDescription="Animation"
/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<RatingBar
android:id="#+id/music_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="40dp"
android:numStars="4"
android:rating="3.5" />
<ImageView
android:id="#+id/btn_playmusic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:src="#drawable/resume" />
</LinearLayout>
OnCreateView
linearTop = (LinearLayout) v.findViewById(R.id.linear_top);
AnimatedGifImageView animatedGifImageView =
(AnimatedGifImageView) linearTop.getChildAt(1); /* Null Exception */
Child views are indexed from 0. In other words, the first child view is 0, the second child view is 1... and so on.
Also, why don't you just get the the ImageView by it's ID?
AnimatedGifImageView musicAnim = (AnimatedGifImageView) findViewById(R.id.music_anim);
#Override
public View onCreateView(View v, String name, Context context,
AttributeSet attrs) {
LinearLayout linearTop = (LinearLayout) v.findViewById(R.id.linear_top);
ImageView animatedGifImageView = (ImageView) linearTop.getChildAt(1); // NullPointer
return super.onCreateView(v, name, context, attrs);
}
Does your code look like this? You are getting a NullPointerException because in onCreateView, the parameter View v may be null, as mentioned in the docs here. Are you using fragments? If not, then don't use onCreateView. Place your code instead in onCreate like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout linearTop = (LinearLayout) findViewById(R.id.linear_top);
ImageView animatedGifImageView = (ImageView) linearTop.getChildAt(1);
setContentView(R.layout.activity_nested_linearlayout);
}
also, as what Sound Conception mentioned, you could just try to get the view directly by using its id instead of manually getting it using an index.
Expected Result
Clicking on the toggle button will show up the menu and slide out the content view rightward. After animation is finished, the layout parameters of the content view gets updated to the final position.
Problem
When updating the final position of the content view, the statement mViewContent.setLayoutParams(params); causes the crash. The error message is java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams
Source Code
Main.java > public class MainActivity extends Activity {}
public void onToggleButtonMenuClicked(View view) {
// Is the toggle on?
boolean toggleTurnedOn = ((ToggleButton) view).isChecked();
if (toggleTurnedOn) { // If the toggle is turned on
// Show menu
LinearLayout mViewMenu = (LinearLayout) findViewById(R.id.linear_layout_menu);
Animation animMenuOn = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_menu_on);
mViewMenu.startAnimation(animMenuOn);
LinearLayout mViewContent = (LinearLayout) findViewById(R.id.linear_layout_content);
Animation animContentOff = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_content_off);
mViewContent.startAnimation(animContentOff);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(480, 800);
params.leftMargin = 384; // Shift 384 pixels from left screen border
params.rightMargin = -96; // Exceed 96 pixels from right screen border
mViewContent.setLayoutParams(params); // This statement causes crash!
} else {
// Hide menu...
} // End of toggle events handling
} // End of onToggleButtonMenuClicked()
activity_main.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="329dp"
android:layout_height="wrap_content" >
<!-- The Menu View -->
<LinearLayout
android:id="#+id/linear_layout_menu"
android:layout_width="263dp"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/table_row_1_search_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:weightSum="10"
android:orientation="horizontal" >
<EditText
android:id="#+id/edit_text_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="7"
android:hint="#string/edit_text_search_id"
android:textSize="14sp" />
<Button
android:id="#+id/button_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="3"
android:text="#string/button_search_id" />
</LinearLayout>
<!-- Other rows in the menu are omitted -->
</LinearLayout> <!-- End of Menu -->
<!-- The Content View -->
<LinearLayout
android:id="#+id/linear_layout_content"
android:layout_width="329dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<ToggleButton
android:id="#+id/toggle_button_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onToggleButtonMenuClicked" />
<TextView
android:id="#+id/text_content"
android:layout_width="480dp"
android:layout_height="wrap_content"
android:text="#string/text_content" />
</LinearLayout> <!-- End of Content -->
</FrameLayout> <!-- End of the root linear layout -->
mViewContent params should be added with resp to your Parent view, suppose you have parent view as LinearLayout then, LinearLayout.LayoutParams must be used.
Explanation (Adapted from here):-
Take for example, LinearLayout.LayoutParams and RelativeLayout.LayoutParams, they are different independent classes. They store different additional information about child views... say..
LinearLayout.LayoutParams can associate weight value with each
view, while RelativeLayout.LayoutParams can't.
RelativeLayout.LayoutParams can have values like alightWithParent,above, below
with each view while LinearLayout.LayoutParams can't.
Although the code will not give compile time error because all LayoutParams have same parent class i.e. ViewGroup.LayoutParams. So, it is always essential, to assign Layout params with respect to parent layout..
make sure you imported the correct layout params
import android.widget.LinearLayout.LayoutParams;
I have layout with controls:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contact_phones_layout_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="phone" />
<Spinner
android:id="#+id/contact_phone_type"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
And I want to inflate it into another layout on the fragment. Quantity of these controls depends on values in a array. Array contains controls objects. So every time onCreateView rises I filled the layout from the array:
private void addLine(PhoneLine line) {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = line.getParent();
((ViewGroup) view.getParent()).removeView(view);
layout.addView(view, layout.getChildCount() - 1);
setButtonVisible(false);
}
if line is null controls are created this way:
private void addLine() {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = inflater.inflate(R.layout.contact_phone_line, layout, false);
EditText phoneEditText = (EditText) view.findViewById(R.id.contact_phone);
Spinner phoneType = (Spinner) view.findViewById(R.id.contact_phone_type);
phoneLines.add(new PhoneLine(phoneEditText, phoneType, view));
layout.addView(view, layout.getChildCount() - 1);
}
But after that I get same values in all EditText/Spinner controls, values equal to last element in the array. What can be wrong? May be there is more pure way to add controls dynamically?
I fixed it by adding controls when onResume rises, not onCreateView. But I still don't understand why onCreateView changes all values.