Sfoglia il codice sorgente

add all needed components, but UI very bad((

MrOzOn 4 anni fa
parent
commit
5607d3a13e

+ 5 - 0
app/build.gradle

@@ -1,6 +1,7 @@
 plugins {
     id 'com.android.application'
     id 'kotlin-android'
+    id 'kotlin-kapt'
 }
 
 android {
@@ -30,6 +31,9 @@ android {
     kotlinOptions {
         jvmTarget = '1.8'
     }
+    buildFeatures {
+        dataBinding true
+    }
 }
 
 dependencies {
@@ -39,6 +43,7 @@ dependencies {
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'com.google.android.material:material:1.2.1'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    implementation 'io.coil-kt:coil:1.1.0'
     testImplementation 'junit:junit:4.+'
     androidTestImplementation 'androidx.test.ext:junit:1.1.1'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

+ 31 - 0
app/src/main/java/com/mrozon/teamviewexample/AvatarAndNameView.kt

@@ -0,0 +1,31 @@
+package com.mrozon.teamviewexample
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import coil.load
+import coil.transform.CircleCropTransformation
+import com.mrozon.teamviewexample.databinding.AvatarAndNameViewBinding
+
+@SuppressLint("ViewConstructor")
+class AvatarAndNameView(context: Context, member: Member) : LinearLayout(context) {
+
+    init {
+        orientation = VERTICAL
+        val binding = AvatarAndNameViewBinding
+            .inflate(LayoutInflater.from(context), this, true)
+        binding.username.text = member.name
+        if (member.picture != null) {
+            binding.userAvatar
+                .load(member.picture) {
+                    transformations(CircleCropTransformation())
+                }
+        } else {
+            binding.userAvatar
+                .load(R.drawable.ic_avatar_placeholder) {
+                    transformations(CircleCropTransformation())
+                }
+        }
+    }
+}

+ 123 - 0
app/src/main/java/com/mrozon/teamviewexample/HalfFieldDrawable.kt

@@ -0,0 +1,123 @@
+package com.mrozon.teamviewexample
+
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.Drawable
+import androidx.core.content.res.ResourcesCompat
+
+class HalfFieldDrawable(context: Context) : Drawable() {
+
+    private val grassColor1 =
+        ResourcesCompat.getColor(context.resources, R.color.grass1, context.theme)
+    private val grassColor2 =
+        ResourcesCompat.getColor(context.resources, R.color.grass2, context.theme)
+    private val grassPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+        style = Paint.Style.FILL
+    }
+    private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+        strokeWidth = 3.px.toFloat()
+        color = ResourcesCompat.getColor(context.resources, R.color.fieldLine, context.theme)
+    }
+    private val rect = Rect()
+    private val padding = 8.px
+    private val halfInnerGoalWidth = 16.px
+    private val halfInnerGoalHeight = 12.px
+    private val halfGoalWidth = 52.px
+    private val halfGoalHeight = 32.px
+    private val cornerSize = 12.px
+    private val middleCircleSize = 24.px
+    private val middleInnerCircleSize = 4.px
+
+    override fun draw(canvas: Canvas) {
+        val width = bounds.width()
+        val height = bounds.height()
+        val rowHeight = height / NUM_OF_GRASS_ROWS
+        canvas.apply {
+            for (i in 0..NUM_OF_GRASS_ROWS) {
+                val top = i * rowHeight
+                rect.set(0, top, width, top + rowHeight)
+                grassPaint.color = if (i % 2 == 0) grassColor1 else grassColor2
+                drawRect(rect, grassPaint)
+            }
+            linePaint.style = Paint.Style.STROKE
+            // draw lines
+            rect.set(padding, padding, width - padding, height - padding)
+            drawRect(rect, linePaint)
+            // draw goal
+            rect.set(
+                (width / 2) - halfInnerGoalWidth,
+                padding,
+                width / 2 + halfInnerGoalWidth,
+                padding + halfInnerGoalHeight
+            )
+            drawRect(rect, linePaint)
+            rect.set(
+                (width / 2) - halfGoalWidth,
+                padding,
+                width / 2 + halfGoalWidth,
+                padding + halfGoalHeight
+            )
+            drawRect(rect, linePaint)
+
+            // draw corner arcs
+            drawArc(
+                padding.toFloat() - cornerSize,
+                padding.toFloat() - cornerSize,
+                (padding + cornerSize).toFloat(),
+                (padding + cornerSize).toFloat(),
+                0f,
+                90f,
+                false,
+                linePaint
+            )
+            drawArc(
+                (width - padding - cornerSize).toFloat(),
+                padding.toFloat() - cornerSize,
+                (width - padding + cornerSize).toFloat(),
+                (padding + cornerSize).toFloat(),
+                90f,
+                90f,
+                false,
+                linePaint
+            )
+            // draw center circle
+            drawArc(
+                (width / 2 - middleCircleSize).toFloat(),
+                (height - padding - middleCircleSize).toFloat(),
+                (width / 2 + middleCircleSize).toFloat(),
+                (height - padding + middleCircleSize).toFloat(),
+                180f,
+                180f,
+                false,
+                linePaint
+            )
+            linePaint.style = Paint.Style.FILL
+            drawArc(
+                (width / 2 - middleInnerCircleSize).toFloat(),
+                (height - padding - middleInnerCircleSize).toFloat(),
+                (width / 2 + middleInnerCircleSize).toFloat(),
+                (height - padding + middleInnerCircleSize).toFloat(),
+                180f,
+                180f,
+                false,
+                linePaint
+            )
+
+        }
+    }
+
+    override fun setAlpha(alpha: Int) {
+        // no-op
+    }
+
+    override fun setColorFilter(colorFilter: ColorFilter?) {
+        // no-op
+    }
+
+    override fun getOpacity(): Int {
+        return PixelFormat.OPAQUE
+    }
+
+}
+
+private const val NUM_OF_GRASS_ROWS = 8

+ 24 - 1
app/src/main/java/com/mrozon/teamviewexample/MainActivity.kt

@@ -1,11 +1,34 @@
 package com.mrozon.teamviewexample
 
+import android.graphics.Color
 import androidx.appcompat.app.AppCompatActivity
 import android.os.Bundle
+import androidx.databinding.DataBindingUtil
+import com.mrozon.teamviewexample.databinding.ActivityMainBinding
 
 class MainActivity : AppCompatActivity() {
+
+    lateinit var binding: ActivityMainBinding
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_main)
+        performDataBinding()
+
+        val members = listOf(
+            Member("player1",23.5,15.7),
+            Member("player2",11.5,25.0),
+            Member("player3",13.5,15.4),
+            Member("player4",3.5,5.0),
+            Member("player5",3.5,5.0),
+            Member("player7",13.5,15.4),
+        )
+
+        binding.teamView.bind(Color.parseColor("#CCCCCC"), members)
     }
+
+    private fun performDataBinding() {
+        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
+        binding.executePendingBindings()
+    }
+
 }

+ 7 - 0
app/src/main/java/com/mrozon/teamviewexample/MainExt.kt

@@ -0,0 +1,7 @@
+package com.mrozon.teamviewexample
+
+import android.content.res.Resources
+
+
+val Int.px: Int
+    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

+ 10 - 0
app/src/main/java/com/mrozon/teamviewexample/Member.kt

@@ -0,0 +1,10 @@
+package com.mrozon.teamviewexample
+
+import android.graphics.drawable.Drawable
+
+data class Member (
+    val name: String = "",
+    val offense: Double = 0.0,
+    val defense: Double = 0.0,
+    val picture: Drawable? = null
+)

+ 48 - 0
app/src/main/java/com/mrozon/teamviewexample/TeamView.kt

@@ -0,0 +1,48 @@
+package com.mrozon.teamviewexample
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import androidx.cardview.widget.CardView
+import com.mrozon.teamviewexample.databinding.TeamViewBinding
+
+class TeamView @JvmOverloads constructor(
+    context: Context, attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0
+) : CardView(context, attrs, defStyleAttr) {
+
+    private val binding = TeamViewBinding
+        .inflate(LayoutInflater.from(context), this, true)
+    private val memberViews: MutableList<View> = mutableListOf()
+
+    init {
+        binding.teamWrapper.background = HalfFieldDrawable(context)
+        radius = 8.px.toFloat()
+    }
+
+    fun bind(teamColor: Int, members: List<Member>) {
+        memberViews.forEach {
+            binding.teamWrapper.removeView(it)
+        }
+        memberViews.clear()
+        binding.apply {
+            teamShirt.imageTintList =
+                ColorStateList.valueOf(teamColor)
+            val attack = members.sumByDouble { it.offense ?: 0.0 }
+            teamAttack.text = attack.toString()
+            val defence = members.sumByDouble { it.defense ?: 0.0 }
+            teamDefence.text = defence.toString()
+            val ids = members.map { member ->
+                val view = AvatarAndNameView(context, member)
+                val generateViewId = View.generateViewId()
+                view.id = generateViewId
+                binding.teamWrapper.addView(view)
+                memberViews.add(view)
+                generateViewId
+            }
+            teamMembersFlow.referencedIds = ids.toIntArray()
+        }
+    }
+}

+ 5 - 0
app/src/main/res/drawable/ic_avatar_placeholder.xml

@@ -0,0 +1,5 @@
+<vector android:height="40dp" android:tint="?attr/colorControlNormal"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/black" android:pathData="M13.49,5.48c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM9.89,19.38l1,-4.4 2.1,2v6h2v-7.5l-2.1,-2 0.6,-3c1.3,1.5 3.3,2.5 5.5,2.5v-2c-1.9,0 -3.5,-1 -4.3,-2.4l-1,-1.6c-0.4,-0.6 -1,-1 -1.7,-1 -0.3,0 -0.5,0.1 -0.8,0.1l-5.2,2.2v4.7h2v-3.4l1.8,-0.7 -1.6,8.1 -4.9,-1 -0.4,2 7,1.4z"/>
+</vector>

+ 4 - 0
app/src/main/res/drawable/ic_shield_small.xml

@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="981.25"
+    android:viewportWidth="981.25" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M946.23,206.651c-0.3,-23 -18,-42 -40.899,-44.101c-190.3,-17.8 -345.601,-119.5 -396.8,-156.7c-10.7,-7.8 -25.2,-7.8 -35.9,0c-51.1,37.2 -206.4,138.9 -396.7,156.7c-22.9,2.101 -40.5,21.101 -40.9,44.101c-2.3,150.1 21.8,659.699 444.1,773.1c7.5,2 15.4,2 22.9,0C924.331,866.451 948.43,356.75 946.23,206.651z"/>
+</vector>

+ 4 - 0
app/src/main/res/drawable/ic_shirt.xml

@@ -0,0 +1,4 @@
+<vector android:height="36dp" android:viewportHeight="455"
+    android:viewportWidth="455" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#CC45" android:pathData="M357.642,32.638l-66.814,-15.896c0,0 -18.622,32.784 -63.328,32.784s-63.328,-32.784 -63.328,-32.784L97.358,32.638L0,119.916l48.555,75.179l48.803,-22.87v266.032h260.284V172.225l48.803,22.87L455,119.916L357.642,32.638z"/>
+</vector>

+ 4 - 0
app/src/main/res/drawable/ic_shirt_outline.xml

@@ -0,0 +1,4 @@
+<vector android:height="40dp" android:viewportHeight="455"
+    android:viewportWidth="455" android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M357.642,32.638l-66.814,-15.896c0,0 -18.622,32.784 -63.328,32.784s-63.328,-32.784 -63.328,-32.784L97.358,32.638L0,119.916l48.555,75.179l48.803,-22.87v266.032h260.284V172.225l48.803,22.87L455,119.916L357.642,32.638z"/>
+</vector>

+ 6 - 0
app/src/main/res/drawable/ic_sword_small.xml

@@ -0,0 +1,6 @@
+<vector android:height="24dp" android:viewportHeight="290.226"
+    android:viewportWidth="290.226" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M63.951,243.575c-1.945,-3.578 -4.401,-6.907 -7.363,-9.869c-3.106,-3.102 -6.626,-5.633 -10.4,-7.63c-4.51,-2.387 -0.945,-7.5 -0.945,-7.5c4.616,-7.023 8.825,-14.079 12.305,-20.226l-23.363,-23.344H11.504c-4.362,0 -7.898,-3.539 -7.898,-7.902c0,-4.361 3.536,-7.9 7.898,-7.9h25.947c2.1,0 4.107,0.832 5.588,2.312l85.379,85.291c1.483,1.483 2.315,3.495 2.315,5.589v26.073c0,4.365 -3.537,7.897 -7.9,7.897c-4.367,0 -7.904,-3.531 -7.904,-7.897v-22.798l-23.27,-23.24c-6.281,3.707 -13.582,8.252 -20.816,13.25C70.842,245.679 66.698,248.629 63.951,243.575z"/>
+    <path android:fillColor="#FF000000" android:pathData="M26.61,237.102c-7.106,0 -13.784,2.764 -18.812,7.784c-5.019,5.015 -7.782,11.686 -7.782,18.778c0,7.097 2.764,13.762 7.782,18.776c5.027,5.016 11.706,7.783 18.812,7.785c7.102,0 13.781,-2.77 18.804,-7.785c5.023,-5.015 7.79,-11.682 7.79,-18.776c0,-7.093 -2.768,-13.764 -7.79,-18.778C40.392,239.866 33.712,237.102 26.61,237.102z"/>
+    <path android:fillColor="#FF000000" android:pathData="M100.985,182.318c-3.502,3.499 -9.232,3.499 -12.734,0.001l-8.81,-8.801c-3.502,-3.498 -3.502,-9.223 0,-12.721L229.832,10.564c3.502,-3.498 10.401,-6.727 15.33,-7.175l36.862,-3.352c4.93,-0.448 8.596,3.218 8.148,8.148l-3.346,36.791c-0.448,4.93 -3.68,11.825 -7.182,15.324l-150.4,150.251c-3.502,3.498 -9.232,3.498 -12.734,0l-8.822,-8.813c-3.502,-3.498 -3.502,-9.223 0,-12.722L233.608,63.213c1.854,-1.848 1.856,-4.852 0.003,-6.702c-1.848,-1.853 -4.853,-1.853 -6.709,-0.002L100.985,182.318z"/>
+</vector>

+ 33 - 14
app/src/main/res/layout/activity_main.xml

@@ -1,18 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".MainActivity">
+    xmlns:tools="http://schemas.android.com/tools">
 
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Hello World!"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+    <data>
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        tools:context=".MainActivity">
+
+        <TextView
+            android:id="@+id/textView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Hello World!"
+            app:layout_constraintBottom_toTopOf="@+id/teamView"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <com.mrozon.teamviewexample.TeamView
+            android:id="@+id/teamView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 37 - 0
app/src/main/res/layout/avatar_and_name_view.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+    </data>
+
+    <merge
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        tools:orientation="vertical"
+        tools:parentTag="android.widget.LinearLayout">
+
+        <ImageView
+            android:id="@+id/user_avatar"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_gravity="center_horizontal"
+            android:importantForAccessibility="no"
+            tools:src="@drawable/ic_avatar_placeholder" />
+
+        <TextView
+            android:id="@+id/username"
+            android:layout_width="80dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="2dp"
+            android:ellipsize="end"
+            android:gravity="center"
+            android:lines="1"
+            tools:text="Футболист1"
+            android:textColor="@color/black"
+            android:textSize="12sp" />
+
+    </merge>
+</layout>

+ 84 - 0
app/src/main/res/layout/team_view.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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>
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/teamWrapper"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="16dp">
+
+        <androidx.constraintlayout.helper.widget.Flow
+            android:id="@+id/team_members_flow"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginEnd="4dp"
+            app:flow_horizontalGap="8dp"
+            app:flow_wrapMode="chain"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:id="@+id/team_shirt_outline"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="16dp"
+            android:src="@drawable/ic_shirt_outline"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <ImageView
+            android:id="@+id/team_shirt"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="16dp"
+            android:importantForAccessibility="no"
+            android:padding="2dp"
+            android:src="@drawable/ic_shirt"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/team_attack"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="4dp"
+            android:drawablePadding="2dp"
+            android:gravity="center"
+            android:textColor="@color/black"
+            android:textSize="18sp"
+            app:drawableEndCompat="@drawable/ic_sword_small"
+            app:drawableTint="@color/black"
+            app:layout_constraintEnd_toStartOf="@+id/team_shirt_outline"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="22.5" />
+
+        <TextView
+            android:id="@+id/team_defence"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="4dp"
+            android:drawablePadding="2dp"
+            android:gravity="center"
+            android:textColor="@color/black"
+            android:textSize="18sp"
+            app:drawableEndCompat="@drawable/ic_shield_small"
+            app:drawableTint="@color/black"
+            app:layout_constraintEnd_toStartOf="@+id/team_shirt_outline"
+            app:layout_constraintTop_toBottomOf="@+id/team_attack"
+            tools:text="17.0" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 3 - 0
app/src/main/res/values/colors.xml

@@ -7,4 +7,7 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+    <color name="grass1">#AEEA00</color>
+    <color name="grass2">#64DD17</color>
+    <color name="fieldLine">#FFFFFF</color>
 </resources>