I've been trying for several days to make a list with the same effect that some Wear OS apps do, such as Google Play Store, where the list is shrinking the items that are disappearing and expanding the new items that are appearing:
I followed the Google Developers documentation, the problem is that when I apply the custom Layout Manager to it, the items don't stay centered for me, they look like this:
This is the code I used:
<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/box_inset_layout_padding"
tools:context=".ui.activity.HomeActivity"
tools:deviceIds="wear">
<androidx.wear.widget.WearableRecyclerView
android:id="#+id/wearable_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.wear.widget.BoxInsetLayout>
private fun setupAdapter() {
val homeOptionsAdapter = HomeOptionsAdapter(petList)
val customScrollingLayoutCallback = CustomScrollingLayoutCallback()
recyclerWearable.layoutManager = WearableLinearLayoutManager(
this,
customScrollingLayoutCallback
)
recyclerWearable.isEdgeItemsCenteringEnabled = true
recyclerWearable.adapter = homeOptionsAdapter
}
private const val MAX_ICON_PROGRESS = 2f
class CustomScrollingLayoutCallback : WearableLinearLayoutManager.LayoutCallback() {
private var progressToCenter: Float = 0f
override fun onLayoutFinished(child: View, parent: RecyclerView) {
child.apply {
// Figure out % progress from top to bottom
val centerOffset = height.toFloat() / 2.0f / parent.height.toFloat()
val yRelativeToCenterOffset = y / parent.height + centerOffset
// Normalize for center
progressToCenter = abs(0.5f - yRelativeToCenterOffset)
// Adjust to the maximum scale
progressToCenter = progressToCenter.coerceAtMost(MAX_ICON_PROGRESS)
scaleX = 1 - progressToCenter.pow(2f)
scaleY = 1 - progressToCenter.pow(2f)
}
}
}
I read that with the option setEdgeItemsCenteringEnabled the list was centered, but I don't see it centered, what can I do?
I have/had the same issue, this is the best work around I could get:
Vid demostration: video link
main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- OPTIONAL -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000"
tools:context=".Main"
android:id="#+id/main">
<!-- REQUIRED-->
<androidx.wear.widget.WearableRecyclerView
android:id="#+id/recycler_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<!-- OPTIONAL -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/top_shadow"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#drawable/gradient_shadow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- OPTIONAL -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/bottom_shadow"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#drawable/gradient_shadow"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
gradient_shadow.xml //OPTIONAL
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<gradient
android:type="linear"
android:centerX="100%"
android:startColor="#DD000000"
android:endColor="#00000000"
android:angle="-90"/>
</shape>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="56sp"
android:layout_marginBottom="3sp"
android:background="#drawable/list_design">
<ImageView
android:id="#+id/imageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="18sp"
android:background="#FF0000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="#tools:sample/avatars" />
<TextView
android:id="#+id/list_main_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10sp"
android:text="List item"
android:textColor="#color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
list_design.item
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/gray"></solid>
<corners android:radius="50dp"/>
</shape>
customScrollingLayoutCallback
// I have this "math" from another app that I was design in javascript for smartphone a long time ago... I dont remember why each part, so, sorry, cant explain hahaha
//I just added the if part and alpha
public void onLayoutFinished(View child, RecyclerView parent) {
int height_parent = parent.getHeight();
float half_height_parent = (float) parent.getHeight() / 2;
int child_max_width = parent.getWidth();
float child_new_width = ((1 - Math.abs((child.getY() - half_height_parent + 45) / height_parent)) * height_parent);
float equivalent_scale = child_new_width / child_max_width;
child.setAlpha(equivalent_scale);
if(child_new_width > (child_max_width * 0.63)) {
child.setScaleX(0.96f);
child.setScaleY(1);
} else {
child.setScaleX(equivalent_scale + 0.2f);
child.setScaleY(0.98f);
}
}
Related
I am using MPAndroid chart library to show pie chart in my app
The legends / chart description labels are not wrapping below one another
I used pieChart.legend.isWordWrapEnabled=true but it doesn't work out
This is my xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".equity.EquityFragment">
<include
android:id="#+id/pageTitleLayout"
layout="#layout/page_title" />
<com.github.mikephil.charting.charts.PieChart
android:id="#+id/pieChart"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/pageTitleLayout" />
<include
android:id="#+id/loader"
layout="#layout/view_progress_loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
And this is code
private fun createChart(chartData:List<EquityPieChart>){
val pieEntry= chartData.map { PieEntry(it.equity,it.name) }
val rnd = Random()
val colors = mutableListOf<Int>()
for (i in chartData.indices){
colors.add(Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)))
}
val dataSet=PieDataSet(pieEntry,getString(R.string.equity_title))
dataSet.colors=colors
binding.pieChart.data = PieData(dataSet)
binding.pieChart.isDrawHoleEnabled=false
binding.pieChart.legend.isWordWrapEnabled=true
binding.pieChart.invalidate()
}
this is the UI i get in device
The text of legends are too big to be fit inside the graph. One way is to keep them outside the graph.
The following attribute can be added to achieve this job:
setDrawInside()
Please use this code:
Legend l = pieChart.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
l.setDrawInside(false);
l.setXEntrySpace(4f);
l.setYEntrySpace(0f);
l.setWordWrapEnabled(true);
This will set the legend outside the graph.
I ended up doing like this with the help of flexboxlayout
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".equity.EquityFragment">
<include
android:id="#+id/pageTitleLayout"
layout="#layout/page_title" />
<com.github.mikephil.charting.charts.PieChart
android:id="#+id/pieChart"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/chartLabels"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/pageTitleLayout" />
<com.google.android.flexbox.FlexboxLayout
android:id="#+id/chartLabels"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:flexWrap="wrap"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="#+id/loader"
layout="#layout/view_progress_loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
Legend layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="25dp"
android:padding="2dp">
<View
android:id="#+id/legendBox"
android:layout_width="10dp"
android:layout_height="10dp"
android:background="#color/primary_500"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="#+id/legendTitle"
app:layout_constraintTop_toTopOf="#+id/legendTitle"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/legendTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
tools:text="xxxxjdsfjsdfj"
android:layout_marginStart="3dp"
app:layout_constraintStart_toEndOf="#+id/legendBox"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Kotlin code
private fun createChart(chartData: List<EquityPieChart>) {
val pieEntry = chartData.map { PieEntry(it.equity) }
val rnd = Random()
val colors = mutableListOf<Int>()
for (i in chartData.indices) {
colors.add(Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256)))
}
val dataSet = PieDataSet(pieEntry, getString(R.string.equity_title))
dataSet.colors = colors
dataSet.valueTextSize = 14f
dataSet.setDrawValues(false)
dataSet.valueTextColor = ContextCompat.getColor(requireContext(), android.R.color.white)
binding.pieChart.data = PieData(dataSet)
binding.pieChart.isDrawHoleEnabled = false
binding.pieChart.description = Description().apply { text="" }
binding.pieChart.invalidate()
binding.pieChart.animateY(1400, Easing.EaseInOutQuad);
binding.pieChart.legend.isEnabled = false
creatChartLabels(colors,chartData)
}
private fun creatChartLabels(colors:List<Int>,chartData: List<EquityPieChart>){
for (i in chartData.indices) {
val view=ItemLegendBinding.inflate(layoutInflater)
view.legendBox.setBackgroundColor(colors[i])
view.legendTitle.text=chartData[i].name
binding.chartLabels.addView(view.root)
}
}
output
I'd like to use CoordinatoryLayout, AppBarLayout and CollapsingToolbarLayout to create a layout that resembles the below example from Google Calendar.
The key things I'm trying to replicate:
Scrolling the content behind the status bar
Rounded corners at the top of the scroll container
Enough room at the top of the screen for the header to not look squashed
The Question
Google Calendar appears to be growing the scroll container as the user scrolls. How would I go about doing this or something similar to achieve the look I'm after?
I've put together a quick example of what I'm trying to build:
activity_scrolling.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context="uk.co.exampleapplication.ScrollingActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<include
android:id="#+id/lay_header"
layout="#layout/layout_header" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/scroll_header_background"
android:elevation="16dp"
android:paddingBottom="12dp">
<TextView
android:id="#+id/sectionTitleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:text="Title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/filter_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:layout_marginEnd="16dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".ScrollingActivity"
tools:showIn="#layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/text_margin"
android:text="#string/large_text" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
layout_header.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingTop="60dp"
android:paddingBottom="40dp"
app:layout_collapseMode="parallax"
tools:ignore="HardcodedText">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="Title"
android:textColor="#FFF"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Subtitle"
android:textColor="#FFF"
android:textSize="16sp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
scroll_header_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
<solid android:color="#FFFFFF" />
<size
android:width="64dp"
android:height="64dp" />
</shape>
My attempt is included below. The header scrolls behind the toolbar as desired but I'd like some additional top padding above my views (about the height of the top inset/ status bar would be sufficient). Google Calendar appears to be solving this by having the container grow as the user scrolls.
Implement an AppBarLayout.OnOffsetChangedListener that will adjust top padding on the ConstraintLayout that holds the TextView and the Button. (Call this view "viewToGrow".) You can also do other things in the listener like change the corner radius of the drawable as the appbar scrolls.
The following example adjusts top padding to give the header more room. This padding is increased as the appbar scrolls up and decreases as it scrolls down. The demo app also removes the corner radius of the drawable during the final 15% of the appbar scroll.
ScrollingActivity
class ScrollingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scrolling)
val viewToGrow: View = findViewById(R.id.viewToGrow)
val baseTopPadding = viewToGrow.paddingTop
// Determine how much top padding has to grow while the app bar scrolls.
var maxDeltaPadding = 0
val contentView = findViewById<View>(android.R.id.content)
ViewCompat.setOnApplyWindowInsetsListener(contentView) { _, insets ->
maxDeltaPadding = insets.systemWindowInsetTop
insets
}
// Get key metrics for corner radius shrikage.
var backgroundRadii: FloatArray? = null
var maxRadius: FloatArray? = null
val backgroundDrawable = (viewToGrow.background as GradientDrawable?)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && backgroundDrawable != null) {
backgroundRadii = backgroundDrawable.cornerRadii
maxRadius = floatArrayOf(backgroundRadii!![0], backgroundRadii[1])
}
// Set up the app bar and the offset change listener.
val appBar: AppBarLayout = findViewById(R.id.app_bar_layout)
val appBarTotalScrollRange: Float by lazy {
appBar.totalScrollRange.toFloat()
}
appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
// Add/remove padding gradually as the appbar scrolls.
val percentOfScrollRange = (-verticalOffset / appBarTotalScrollRange)
val deltaPadding = maxDeltaPadding * percentOfScrollRange
val newTopPadding = baseTopPadding + deltaPadding.toInt()
if (newTopPadding != viewToGrow.paddingTop) {
viewToGrow.setPadding(
viewToGrow.paddingLeft,
newTopPadding,
viewToGrow.paddingRight,
viewToGrow.paddingBottom
)
// Change the drawable radius as the appbar scrolls.
if (backgroundRadii != null && maxRadius != null) {
val radiusShrinkage = if (percentOfScrollRange > (1.0f - CORNER_SHRINK_RANGE)) {
(1.0f - percentOfScrollRange) / CORNER_SHRINK_RANGE
} else {
1.0f
}
backgroundRadii[0] = maxRadius[0] * radiusShrinkage
backgroundRadii[1] = maxRadius[1] * radiusShrinkage
backgroundRadii[2] = maxRadius[0] * radiusShrinkage
backgroundRadii[3] = maxRadius[1] * radiusShrinkage
backgroundDrawable!!.cornerRadii = backgroundRadii
}
}
})
}
companion object {
const val CORNER_SHRINK_RANGE = 0.15f
}
}
I'm trying to create a design with multiple hexagon shape buttons.I'm able to create a single hexagon button, but in my case i have a list of items which need to be shown in a design pattern like below.
if such list design is possible through RecyclerView that would be much better.
A bit tricky but here is how i solved it.
Have a recyclerView like below
<android.support.v7.widget.RecyclerView
android:id="#+id/hexa_rcv"
android:layout_margin=""#dimen/hexa_dp""
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
make 2 folders inside res "values-sw360dp" and "values-sw400dp". create dimens.xml in both the folders. dimens.xml inside values-sw360dp should have
<resources>
<dimen name="margin_16_dp">16dp</dimen>
<dimen name="hexa_dp">25dp</dimen>
</resources>
dimens.xml inside values-sw400dp should have
<resources>
<dimen name="margin_16_dp">16dp</dimen>
<dimen name="hexa_dp">55dp</dimen>
</resources>
And one item for the recyclerView like below
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tv_1"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:textColor="#color/white"
android:textSize="18sp"
android:text="ICU_HDW"
android:background="#drawable/hexagon"/>
Next have a get the reference of recyclerView and set GridaLayoutManager with column size as 3. make every 5th item's span size as 2(so that 4th item will have span size 1 and 5th item will have size 2 hence both together will complete the row and next item will be placed in next row)
I have used kotlin here you can convert to java
RecyclerView hexaRcv = (RecyclerView) findViewById(R.id.hexa_rcv);
GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
int size = 1;
if ((position + 1) % 5 == 0){
size = 2;
}
return size;
}
});
hexaRcv.setLayoutManager(manager);
hexaRcv.setAdapter(new GridAdapter());
Below is the GridAdapter ()
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.HexagonHolder> {
#Override
public GridAdapter.HexagonHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.hexa_tv, parent, false);
return new HexagonHolder(view);
}
#Override
public void onBindViewHolder(GridAdapter.HexagonHolder holder, int position) {
int pos = position + 1;
int topMargin = pxFromDp(holder.textView.getContext(), -17);
int leftMargin = pxFromDp(holder.textView.getContext(), 51); //3 times of 17
GridLayoutManager.LayoutParams param = (GridLayoutManager.LayoutParams) holder.textView.getLayoutParams();
if (pos < 4) {
param.setMargins(0, 0, 0, 0);
} else if ((pos + 1) % 5 == 0 || pos % 5 == 0) {
param.setMargins(leftMargin, topMargin, 0, 0);
} else {
param.setMargins(0, topMargin, 0, 0);
}
holder.textView.setLayoutParams(param);
}
#Override
public int getItemCount() {
return 17;
}
static class HexagonHolder extends RecyclerView.ViewHolder {
TextView textView;
HexagonHolder(View v) {
super(v);
textView = v.findViewById(R.id.tv_1);
}
}
private int pxFromDp(final Context context, final float dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}
for first 3 items no margin. After that all item will have a negative top margin of 75 (i.e. -75px). every 4th and 5th item will not only have top margin of -75 but will also have a left margin of 165.
You can use constant or actually calculate them based on screen width or use dip.
Below is the result
Below is the hexagon.xml save it in drawable folder
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#148275"
android:pathData="M485.291,129.408l-224-128c-3.285-1.877-7.296-1.877-10.581,0l-224,128c-3.328,1.899-5.376,5.44-5.376,9.259v234.667
c0,3.819,2.048,7.36,5.376,9.259l224,128c1.643,0.939,3.456,1.408,5.291,1.408s3.648-0.469,5.291-1.408l224-128
c3.328-1.899,5.376-5.44,5.376-9.259V138.667C490.667,134.848,488.619,131.307,485.291,129.408z" />
</vector>
You can try something like that. It needs to be improved as the gap between the two lines is not constant depending on the screen size:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
<include
layout="#layout/partial_squared"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
partial_squared.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
layout="#layout/partial_first_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
layout="#layout/partial_second_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/line_height_80" />
</RelativeLayout>
partial_first_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView style="#style/InlinedImageView" />
<ImageView style="#style/InlinedImageView" />
<ImageView style="#style/InlinedImageView" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">
<TextView
style="#style/InlinedTextView"
android:text="case 1" />
<TextView
style="#style/InlinedTextView"
android:text="case 2" />
<TextView
style="#style/InlinedTextView"
android:text="case 3" />
</LinearLayout>
</RelativeLayout>
partial_second_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3">
<Space
android:layout_width="0dp"
android:layout_height="15dp"
android:layout_weight=".5" />
<ImageView style="#style/InlinedImageView" />
<ImageView style="#style/InlinedImageView" />
<Space
android:layout_width="0dp"
android:layout_height="15dp"
android:layout_weight=".5" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">
<Space
android:layout_width="0dp"
android:layout_height="15dp"
android:layout_weight=".5" />
<TextView
style="#style/InlinedTextView"
android:text="case 1" />
<TextView
style="#style/InlinedTextView"
android:text="case 2" />
<Space
android:layout_width="0dp"
android:layout_height="15dp"
android:layout_weight=".5" />
</LinearLayout>
</RelativeLayout>
dimens.xml
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="line_height">110dp</dimen>
<dimen name="line_height_80">72dp</dimen>
<dimen name="margin_in_between">2dp</dimen>
</resources>
styles.xml
<style name="InlinedImageView">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">#dimen/line_height</item>
<item name="android:layout_weight">1</item>
<item name="android:src">#drawable/triangle</item>
<item name="android:paddingLeft">#dimen/margin_in_between</item>
<item name="android:paddingRight">#dimen/margin_in_between</item>
</style>
<style name="InlinedTextView">
<item name="android:gravity">center</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
</style>
Result
I'm using CardView in a CardListView.
I would like to know how to highlight a card item when it's pressed just like in the "CardsLibDemo Extras" application, precisely in the "Card and Weather List" fragment when you press on a card from the "You may know" list ?
P.S. : I'm using API 15 (Android 4.0.4 Ice Cream sandwich) to test this application.
This is my fragment layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- You can customize this layout.
You need to have in your layout a `CardView` with the ID `list_cardId` -->
<it.gmariotti.cardslib.library.view.CardListView
xmlns:card="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rent_list"
android:foreground="?android:attr/selectableItemBackground"
card:list_card_layout_resourceID="#layout/card_row_layout_rent_list" />
</LinearLayout>
Here is the card_row_layout_rent_list.xml :
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<it.gmariotti.cardslib.library.view.CardView
xmlns:card="http://schemas.android.com/apk/res-auto"
android:id="#+id/list_cardId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card:card_layout_resourceID="#layout/card_layout"
style="#style/list_card.base"/>
</LinearLayout>
Here is the card_layout.xml :
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Card visible layout -->
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/card_main_layout"
style="#style/card.main_layout">
<it.gmariotti.cardslib.library.view.component.CardThumbnailView
xmlns:card="http://schemas.android.com/apk/res-auto"
style="#style/card_thumbnail_outer_layout"
android:id="#+id/card_thumbnail_layout"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card:card_thumbnail_layout_resourceID="#layout/card_image" />
<FrameLayout
android:id="#+id/card_main_content_layout"
android:layout_gravity="right"
style="#style/card.content_outer_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- Compound view for Shadow
If you want to customize this element use attr card:card_shadow_layout_resourceID -->
<it.gmariotti.cardslib.library.view.component.CardShadowView
style="#style/card.shadow_outer_layout"
android:id="#+id/card_shadow_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/card_content_expand_layout"
style="#style/card.main_contentExpand"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Here is the card_inner_layout_rent_list.xml :
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- This is the base Inner View inside a CardHeader.
You can customize it with your layout xml file and your CardHeader.
You can popolate your elements with CardHeader#setupInnerViewElements(android.view.ViewGroup, android.view.View) method -->
<TextView
android:id="#+id/textViewPrix"
style="#style/card.header_simple_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="2dp"
android:text="#string/card_txtPrix"
android:textColor="#color/blue"
android:textStyle="bold" />
<TextView
android:id="#+id/textViewAdr"
style="#style/card.expand_simple_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="2dp"
android:text="#string/card_txtAdr"
android:textColor="#color/black"
android:textSize="#dimen/card_expend_simple_title_text_size_16sp"
android:textStyle="bold" />
<TextView
android:id="#+id/textViewType"
style="#style/card.expand_simple_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="2dp"
android:text="#string/card_txtType"
android:textColor="#color/black"
android:textSize="#dimen/card_expand_simple_title_text_size_12sp" />
<TextView
android:id="#+id/textViewSup"
style="#style/card.expand_simple_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="1dp"
android:text="#string/card_txtSup"
android:textColor="#color/black"
android:textSize="#dimen/card_expand_simple_title_text_size_12sp" />
<TextView
android:id="#+id/textViewHonor"
style="#style/card.expand_simple_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginTop="0dp"
android:text="#string/card_txtHonor"
android:textSize="#dimen/card_expand_simple_title_text_size_12sp" />
</LinearLayout>
Here is the card_image.xml :
<?xml version="1.0" encoding="UTF-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_thumbnail_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="1dp"
android:layout_weight="0"
android:layout_gravity="center"
android:contentDescription="#string/imageview_desc"
android:orientation="vertical"/>
Finally, here's where I fill CardList (extracted from the fragment class) :
private void initColorCard() {
int listImages[] = new int[]{R.drawable.angry_1, R.drawable.angry_2,
R.drawable.angry_3, R.drawable.angry_4, R.drawable.angry_5};
ArrayList<Card> cards = new ArrayList<Card>();
for (int i = 0; i<5; i++) {
// Create a Card
ColorCard card = new ColorCard(getActivity());
//Set onClick listener
card.setOnClickListener(new Card.OnCardClickListener() {
#Override
public void onClick(Card card, View view) {
Toast.makeText(getActivity(), "Clickable card : " + card.getCardHeader().getTitle(), Toast.LENGTH_SHORT).show();
}
});
// Create a CardHeader
CardHeader header = new CardHeader(getActivity());
// Add Header to card
header.setTitle("" + i);
card.addCardHeader(header);
CardThumbnail thumb = new CardThumbnail(getActivity());
thumb.setDrawableResource(listImages[i]);
card.addCardThumbnail(thumb);
cards.add(card);
}
CardArrayAdapter mCardArrayAdapter = new CardArrayAdapter(getActivity(), cards);
CardListView listView = (CardListView) getActivity().findViewById(R.id.rent_list);
if (listView != null) {
listView.setAdapter(mCardArrayAdapter);
}
}
Any idea what's missing in this code so any pressed card become highlighted ?
If you are using the CardViewNative, you have to customize this style:
<style name="card.native.main_layout_foreground">
<item name="android:foreground">?android:selectableItemBackground</item>
<item name="android:background">#color/card_native_background</item>
<item name="android:layout_marginTop">#dimen/card_main_layout_native_view_margin_top</item>
<item name="android:layout_marginBottom">#dimen/card_main_layout_native_view_margin_bottom</item>
<item name="android:layout_marginLeft">#dimen/card_main_layout_native_view_margin_left</item>
<item name="android:layout_marginRight">#dimen/card_main_layout_native_view_margin_right</item>
</style>
Otherwise, if you are using the old CardView you can read this:
https://github.com/gabrielemariotti/cardslib/blob/master/doc/CARD.md#customize-card-background
I have a drawable resource drawable/badge.xml which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
<solid android:color="#000" />
</shape>
...and is used inside a layout file layout/badge.xml which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="18dp"
android:layout_height="18dp" >
<View
android:id="#+id/badge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/badge" />
<TextView
android:id="#+id/badge_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:textColor="#fff"
android:textSize="12sp"
android:textStyle="bold" />
</RelativeLayout>
Inside a BaseExpandableListAdapters getChildView method there is this code:
...
LinearLayout badgeContainer = (LinearLayout) convertView.findViewById(R.id.badge_container);
for (String property : properties) {
RelativeLayout badge = (RelativeLayout) inflater.inflate(R.layout.badge, badgeContainer, false);
badge.setLayoutParams(new LinearLayout.LayoutParams(50, 50));
((TextView) badge.findViewById(R.id.badge_text)).setText(course.property);
badge.findViewById(R.id.badge).getBackground().setColorFilter(getColorResourceByProperty(property), PorterDuff.Mode.DST);
badgeContainer.addView(badge);
}
...
So what I'm trying to do is change the background color of the drawable badge for each property, but this code still yields in all badges to have a black background. What am I doing wrong? I've also tried many other PorterDuff modes without results.
After fighting some more I got it working like this:
drawable/circle.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#fff" />
</shape>
layout/badge.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/badge_text"
android:layout_width="16dp"
android:layout_height="16dp"
android:gravity="center_vertical|center_horizontal"
android:background="#drawable/circle"
android:textColor="#color/white"
android:textSize="12sp"
android:layout_marginLeft="1dp"
android:textStyle="bold" />
and inside the adapter:
LinearLayout badgeContainer = (LinearLayout) convertView.findViewById(R.id.badge_container);
for (int i = 0; i < course.properties.length; i++) {
TextView badge = (TextView) inflater.inflate(R.layout.badge, badgeContainer, false);
badge.setText(course.properties[i]);
int colorID = context.getResources().getIdentifier("property_" + course.properties[i], "color", context.getPackageName());
badge.getBackground().setColorFilter(context.getResources().getColor(colorID), Mode.MULTIPLY);
badgeContainer.addView(badge);
}