I have a Coordinator Layout like so:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinator_subreddit_selection"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.clans.fab.FloatingActionMenu
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
And in my code, I have a snackbar like so:
Snackbar.make(getView().findViewById(R.id.addFabMenu),
R.string.no_entered_subreddit_name, Snackbar.LENGTH_SHORT).show();
Here is what I get:
And here is what I what:
I've tried copying the code from the example activity exactly but it still does not work like the example activity.
Fixed it by creating a Coordinator behaviour like so:
public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
private static final boolean SNACKBAR_BEHAVIOR_ENABLED;
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
static {
SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
}
}
And extended my view class, applying the behaviour to it
#CoordinatorLayout.DefaultBehavior(MoveUpwardBehavior.class)
public class MoveUpwardsFloatingMenu extends FloatingActionMenu {
public MoveUpwardsFloatingMenu(Context context) {
super(context);
}
public MoveUpwardsFloatingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MoveUpwardsFloatingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
Try using the standard FloatingActionButton from the support library
Instead of:
<com.github.clans.fab.FloatingActionMenu
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
Use:
<android.support.design.widget.FloatingActionButton
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
CoordinatorLayout.DefaultBehavior annotation was deprecated from API
level 27.1.0.
To add a Behavior to your custom View, you just need to implement CoordinatorLayout.AttachedBehavior interface to return the default behavior or using layout_behavior attribute that is present will override the AttachedBehavior.
Here you can see an example:
class MoveUpwardBehavior : CoordinatorLayout.Behavior<View>() {
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: View,
dependency: View): Boolean = dependency is SnackbarLayout
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: View,
dependency: View
): Boolean {
child.translationY = min(DEFAULT_CHILD_SIZE, dependency.translationY - dependency.height)
return true
}
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
super.onDependentViewRemoved(parent, child, dependency)
child.translationY = DEFAULT_CHILD_SIZE
}
companion object {
private const val DEFAULT_CHILD_SIZE = 0f
}
}
class ExampleComponentView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), CoordinatorLayout.AttachedBehavior {
.....
override fun getBehavior(): CoordinatorLayout.Behavior<*> =
MoveUpwardBehavior()
}
Related
I'm trying to implement an empty state recycler view based on this post. I have migrated the solution to Kotlin, but the problem is I'm not able to extend CustomRecyclerView.Adapter (Adapter is an abstract class defined in RecyclerView) from the newly defined custom recycler view in Kotlin. And I have observed the same CustomRecyclerView.Adapter can be extended in Java.
Custome RecyclerView implementation
open class CustomRecyclerView: RecyclerView {
private var emptyStateView : View? = null
constructor(context: Context) : super(context)
constructor(context: Context , attrs: AttributeSet) : super(context,attrs)
constructor(context: Context , attrs: AttributeSet, defstyle: Int) : super(context,attrs,defstyle)
var observer: AdapterDataObserver = object : AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
initEmptyView()
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
super.onItemRangeRemoved(positionStart, itemCount)
initEmptyView()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount)
initEmptyView()
}
}
private fun initEmptyView() {
emptyStateView?.let {
it.visibility = if (adapter == null || adapter!!.itemCount == 0) View.VISIBLE else View.GONE
this#CustomRecyclerView.visibility = if (adapter == null || adapter!!.itemCount == 0) View.GONE else View.VISIBLE
}
}
override fun setAdapter(adapter: Adapter<*>?) {
val oldAdapter = getAdapter()
super.setAdapter(adapter)
oldAdapter?.unregisterAdapterDataObserver(observer)
adapter?.registerAdapterDataObserver(observer)
}
/**
* #param emptyView is the view which is going to display when the recycler view is empty
* **/
fun setEmptyView(emptyView: View) {
this.emptyStateView = emptyView
initEmptyView()
}}
Adding images for the extension implementation in java and kotin
To summarise the comments: inherit from RecyclerView.Adapter, see below
YourAdapter: RecyclerView.Adapter<YourViewHolder>()
And just a hint, here's the Kotlin way to extend a class with multiple constructors:
class CustomRecyclerView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr)
For more info, check #JvmOverloads
It should be RecyclerView.Adapter.. and set adapter to customRecyclerView.
I have an Android spinner that I would like to disable when there's less than one item in my list. What I mean by that is I would like the title to show up, but I don't want a list to drop down when I click it.
I've tried isClickable = false, and isEnabled = false like in many of the posts out there, but I still can't get it to work.
I successfully get ride of the spinner dropdown icon, but I can still click the text and get the list.
class CustomSpinnerAdapter(private val ctx: Context, val locations: List<Location>) : ArrayAdapter<Location>(ctx, R.layout.list_item_spinner_view) {
override fun isEmpty() = locations.isEmpty()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val context = parent.context
val inflater = LayoutInflater.from(context)
var view = convertView
if (view == null)
view = inflater.inflate(R.layout.list_item_spinner_view, parent, false)
view!!.location_spinner_name.text = locations[position].name
if (count < 2) {
view.location_spinner.visibility = View.GONE
view.better_name.isEnabled = false
view.better_name.isClickable = false
}
view.location_spinner_name.typeface = Typeface.createFromAsset(ctx.assets, ctx.getString(R.string.font_bold))
return view
}
Here is the view for my title
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="#+id/better_name">
<TextView
android:id="#+id/location_spinner_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
style="#style/TextAppearance.Widget.AppCompat.Toolbar.Title"/>
<com.ge.cbyge.view.TintableImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_arrow_drop_down_black"
android:tint="#color/medium_gray"
android:paddingTop="#dimen/activity_home_spinner_dropdown_padding_top"/>
</LinearLayout>
Hopefully this will be helpful. It is not a re-write of your app, but maybe an idea that you can integrate:
You could create your own Spinner class like this:
class MySpinner extends AppCompatSpinner {
public MySpinner(Context context) {
super(context);
}
public MySpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean performClick() {
if ( getCount() == 0 ) {
return true; // we do nothing because adapter contents = 0
}
else {
return super.performClick(); // we proceed as normal
}
}
}
Then alter your XML references from "Spinner" to "[yourPackageName].MySpinner"
For instance I have a custom button and want to connect it to a SeekBar:
public class SeekBarButton extends ImageButton {
SeekBar seekBar;
public SeekBarButton(Context context) {
super(context);
}
public SeekBarButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SeekBarButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSeekBar(SeekBar seekBar) {
this.seekBar = seekBar;
}
public SeekBar getSeekBar() {
return seekBar;
}
}
I can do it in the code:
sbb = (SeekBarButton) rootView.findViewById(R.id.minus_red);
sbRed = (SeekBar) rootView.findViewById(R.id.sbRed);
sbb.setSeekBar(sbRed);
But 8 buttons will give a lot of boilerplate, and I want something like:
<com.whatever.views.SeekBarButton
...
whatToPutHere:seekbar="#+id/sbRed" // like this? whatToPutHere?
android:id="#+id/minus_red" />
<SeekBar
android:id="#+id/sbRed"
... />
The easiest way to do this is to create a custom ViewGroup that contains both the Button and Seekbar. If you cannot do that, for any reason, here's a solution:
There are a few steps to make this work. First you must define a custom XML attribute that you can then reference and use.
Edit (or create) res/values/attrs.xml. Add:
<declare-styleable name="SeekBarButton">
<attr name="seekbarId" format="integer" />
</declare-styleable>
Then, in SeekBarButton, call this from the constructors:
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SeekBarButton, defStyleAttr, 0);
mSeekbarId = a.getResourceId(R.styleable.SeekBarButton_seekbarId, 0);
a.recycle();
}
}
Finally, in your root ViewGroup of your layout file, add
xmlns:app="http://schemas.android.com/apk/res-auto"
Then,
<com.whatever.views.SeekBarButton
android:id="#+id/minus_red"
app:seekbarId="#+id/sbRed"
... />
<SeekBar
android:id="#+id/sbRed"
... />
Note You will need to call ((ViewGroup) getParent()).findViewById(mSeekbarId) in SeekBarButton to instantiate the SeekBar, but getParent() will be null in SeekBarButton constructors. So, delay findViewById() until you need the SeekBar.
I think you are close. In the first XML tag of your layout file (my example is a RelativeLayout) you need this reference to "custom":
<RelativeLayout
xmlns:custom="http://schemas.android.com/apk/res-auto">
Then farther down wherever your custom ImageButton is, you need this:
<com.whatever.views.SeekBarButton
...
custom:seekbar="#+id/sbRed"
android:id="#+id/minus_red" />
You will also need a seekBarButton.xml file in your project\res\values folder, if you didn't already know that.
I got a class to extend NumberPicker component to introduce min & max value:
public class ExtendedNumberPicker extends NumberPicker {
public ExtendedNumberPicker(Context context) {
super(context);
}
public ExtendedNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
processAttributeSet(attrs);
}
public ExtendedNumberPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
processAttributeSet(attrs);
}
private void processAttributeSet(AttributeSet attrs) {
//This method reads the parameters given in the xml file and sets the properties according to it
this.setMinValue(attrs.getAttributeIntValue(null, "min", 0));
this.setMaxValue(attrs.getAttributeIntValue(null, "max", 0));
}
}
I put it into layout:
<com.example.myapp.component.ExtendedNumberPicker
android:id="#+id/pick_hh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView1"
android:layout_below="#+id/textView1"
android:layout_marginTop="16dp"
min="140"
max="200" />
In LogCat, it shows:
Tag Text
---------------------------
PropertyValuesHolder Can't find native method using JNI, use reflectionjava.lang.NoSuchMethodError: no method with name='setSelectorPaintAlpha' signature='(I)V' in class Lcom/example/myapp/component/ExtendedNumberPicker;
PropertyValuesHolder Couldn't find setter/getter for property selectorPaintAlpha with value type int
I know JNI is Java Native Interface, but I never use/see the property selectorPaintAlpha. What is it? How to resolve the issue?
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/titleBarBG"
android:layout_alignParentLeft="true" >
<RelativeLayout
android:id="#+id/scrollContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<GridView
android:id="#+id/issueList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/archiveTitle"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="#drawable/customshape"
android:numColumns="3"
android:overScrollMode="never"
android:scrollbars="none" >
</GridView>
</RelativeLayout>
</ScrollView>
I would like to create a gridview that act like a table . For example, the size of the grid will increase that will make the gridview taller. Instead of hide the extra content ,I would like the grid view show all content and expand the height when there is additional content
How to implement this? thanks
public class MyGridView extends GridView {
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
This is slightly cleaned up version of: Grid of images inside ScrollView
WrappedGridView.java:
/**
* Use this class when you want a gridview that doesn't scroll and automatically
* wraps to the height of its contents
*/
public class WrappedGridView extends GridView {
public WrappedGridView(Context context) {
super(context);
}
public WrappedGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrappedGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Calculate entire height by providing a very large height hint.
// View.MEASURED_SIZE_MASK represents the largest height possible.
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
Include in an XML layout like you would a GridLayout. Use an adapter to provide it views.
As far as I can tell, this is the simplest solution available now. There no other view available in the framework that handles wrapping. It would be nice if someone were to provide an elegant, automatically sizing table view. Modifying GridView.java for this purpose may not be a bad idea.
Alternatively, you may find one of the 'FlowLayout' projects acceptable. There is android-flowlayout and FlowLayout. These are a little more flexible than a simple grid and, I assume, a little less efficient. You also shouldn't need to provide them an adapter.