I have Custom View called LoadingButton extending FrameLayout. This class is inflatin custom XML layout containing LinearLayout and ProgressBar with parent FrameLayout. This should be functional as Button. I have custom defined styles for each Button type (changing background, tint, text color ...). I want to pass this style into my Custom View class. But it doesnt work. This type of constructor is not called. Instead general construtor with Context and AttributeSet is called.
Any explanation? I though if I add style into my custom View it will be automatically detected and different type of constructor is called.
Constructors:
#SuppressLint("ClickableViewAccessibility")
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
parseAttrs(context, attrs)
}
#SuppressLint("ClickableViewAccessibility")
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
parseAttrs(context, attrs, defStyleAttr)
}
Usage of Custom View:
<com.project.custom.LoadingButton
android:id="#+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/button_blue_filled"
app:l_buttonText="#string/text"
android:layout_marginTop="48dp" />
Related
I have a big library of custom components that I want to use in my Android app.
All components use custom attributes to customize its content.
This is a sample component declared in a XML file:
<myapp.CustomTextView
android:id="#+id/text_view_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:layout_marginBottom="8dp"
custom:textContent="This is my text" />
This is the custom attributed declared:
<declare-styleable name="CustomTextView">
<attr name="textContent" format="string" />
</declare-styleable>
And this is the implementation of the custom component:
public class CustomTextView extends TextView {
private Context context;
public CustomTextView(Context context) {
this(context, null);
}
public CustomTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
readAttrs(context, attrs);
}
private void readAttrs(Context context, AttributeSet attrs) {
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTextView,0, 0);
try {
String content = array.getString(R.styleable.CustomTextView_textContent);
initContent(context, content);
} finally {
array.recycle();
}
}
private void initContent(Context context, String content) {
...
}
}
The problem is that I'm using data binding in my app, so if I do this, the app doesn't compile:
<myapp.CustomTextView
android:id="#+id/text_view_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
android:layout_marginBottom="8dp"
custom:textContent="#{mainView.content}" />
After reading a lot, the solution seems to be to create a bindingadapter for that custom attribute, but in my case, create a binding adapter for every custom attribute would be a huge work because there are tons of components and attributes.
Is there a way to reduce this amount of work or to adapt data binding to these custom attributes?
Using:
com.google.android.material:material:1.4.0
If I have a layout xml file in a fragment/activity with a TextView:
<TextView
style="#style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
If I go between light/dark mode the text color switches appropriately.
However if I create a custom view:
class MyView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyle, defStyleRes) { ... }
And that view contains the same textview, the text color does not change in night mode.
I initialize the view from a fragment:
new MyView(this.getActivity().getBaseContext());
I have also tried to directly apply the base theme:
new MyView(this.getActivity().getBaseContext(), null, R.id.AppTheme);
In addition for some strange reason I can work around this issue by creating my own text colors in the appropriate light/dark folders and that picks up the change between light/dark:
<TextView
style="#style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/my_text_color" />
Turns out I was passing the wrong context.
When creating the view:
new MyView(this.getActivity().getBaseContext());
Should be:
new MyView(this.getActivity());
How can I make last visible items of recycler view to have a slight different style. For example, I would like them to appear like this:
As you can see, the last 3 items are grayed out proportionally. I have searched the internet but no success. Any suggestions would be greatly appreciated!
Find out the size of the list you are using to populate the recyler view, this may have been set in the adapter constructor. I'll call it myList.
then in your onBindViewHolder something like:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
....
if (myList.size() - position < 3) {
holder.wordRow.setTextColor(this.mContext.getResources().getColor(R.color.light_gray));
}
Where wordRow is a TextView element
You can create a custom recyclerView layout and then set the topFadingEdgeLength to 0.0. This will allow you to only give effect in the bottom, else it will be applied to the top also.
In your XML
<com.example.BottomFadeEdgeRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="vertical"
android:fadingEdgeLength="180dp"
android:requiresFadingEdge="vertical">
</com.example.BottomFadeEdgeRecyclerView>
I have set the fadingEdgeLength to 180dp. You can set the desired value
And the custom view class
import android.content.Context
import android.util.AttributeSet
import androidx.recyclerview.widget.RecyclerView
class BottomFadeEdgeRecyclerView : RecyclerView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getTopFadingEdgeStrength(): Float {
return 0.0f
}
}
you can create a custom scrollView layout and then set the topFadingEdgeLength to 0.0. This will allow you to only give effect in the bottom, else it will be applied to the top also.
You can use the scrollView to wrap your recyclerView
In the XML set your scrollView as below
<com.example.BottomFadeEdgeScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="vertical"
android:fadingEdgeLength="180dp"
android:requiresFadingEdge="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.example.BottomFadeEdgeScrollView>
I have set the fadingEdgeLength to 180dp. You can set the desired value
Here's the file in Kotlin
import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
class BottomFadeEdgeScrollView : ScrollView {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun getTopFadingEdgeStrength(): Float {
return 0.0f
}
}
I need to create my own ImageView.
This is my class:
public class Brick extends ImageView implements Serializable{
public Brick(Context context) {
super(context);
}
public Brick(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Brick(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
When I try to use my ImageView inside a xml layout file (as you can see below) I have a problem. I can see a black shape, but there is not the image (the drawable called d) inside it.
<com.myapp.Brick
android:id="#+id/myBrick"
app:srcCompat="#drawable/d"
android:background="#000000"
android:layout_width="300dp"
android:layout_height="300dp" />
What's my error?
You should call android:src="#drawable in your XML Section .
<com.myapp.Brick
android:id="#+id/myBrick"
app:srcCompat="#drawable/d"
android:background="#000000" // showing Black Shape Background
android:layout_width="300dp"
android:layout_height="300dp"
android:src="#drawable/add_image /> // Add android:src
You should add app:srcCompat or android:src in your XML Section
I have an activity with a button on it:
<Button
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:id="#+id/some_id" />
and I can find it just fine from the activity's source:
button = (Button)findViewById(R.id.some_id); // returns the button
but if I switch the button for a custom control:
<FancyButton
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:id="#+id/some_id" />
then I start getting null instead:
fancyButton = (FancyButton)findViewById(R.id.some_id); // returns null
Fancy button is really just an empty view at the moment, so I'm not sure what could cause it to fail like this:
public FancyButton(Context context, AttributeSet attrs) {
super(context);
LayoutInflater.from(context).inflate(R.layout.fancy_button, this, true);
}
I definitely missed something obvious.
I forgot to forward the attributes parameter to the base constructor. Since the id is an attribute, it ended up dropped on the floor instead of set.
Wrong:
public FancyButton(Context context, AttributeSet attrs) {
super(context);
Right:
public FancyButton(Context context, AttributeSet attrs) {
super(context, attrs);
Turning on "unused parameter" warnings would have caught this faster.