From 5940badb88e6a24f4afdcdea958a25d29b89887a Mon Sep 17 00:00:00 2001 From: kareemradwan Date: Sat, 4 Jul 2020 20:14:02 +0300 Subject: [PATCH 1/2] add number stepper version --- .idea/misc.xml | 2 +- .../com/kareemradwan/stepeer/MainActivity.kt | 10 +- .../ic_signal_wifi_0_bar_black_24dp.xml | 5 + .../ic_signal_wifi_4_bar_black_24dp.xml | 5 + app/src/main/res/layout/activity_main.xml | 18 +- app/src/main/res/layout/order_step.xml | 4 +- app/src/main/res/values/colors.xml | 1 + stepeer/build.gradle | 9 +- stepeer/src/main/AndroidManifest.xml | 2 +- .../com/kradwan/stepeer/model/StepColor.kt | 12 + .../com/kradwan/stepeer/model/StepDrawable.kt | 10 +- .../kradwan/stepeer/view/NumberStepperView.kt | 274 ++++++++++++++++++ .../stepeer/view/SingleNumberStepView.kt | 167 +++++++++++ .../src/main/res/drawable/bg_rounded_fill.xml | 11 + .../src/main/res/drawable/custom_checkbox.xml | 5 +- stepeer/src/main/res/layout/item_num_step.xml | 75 +++++ stepeer/src/main/res/layout/item_step.xml | 2 + stepeer/src/main/res/values/attrs.xml | 15 + 18 files changed, 616 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/drawable/ic_signal_wifi_0_bar_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_signal_wifi_4_bar_black_24dp.xml create mode 100644 stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt create mode 100644 stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt create mode 100644 stepeer/src/main/res/drawable/bg_rounded_fill.xml create mode 100644 stepeer/src/main/res/layout/item_num_step.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index a76efc8..9159540 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt b/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt index cd13fd4..08aecdd 100644 --- a/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt +++ b/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt @@ -15,7 +15,7 @@ class MainActivity : AppCompatActivity(), SteeperView.SteeperHandler { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - val order1 = Order(1, "Step1" , true) + val order1 = Order(1, "Step1", true) order1.setChecked(true) val adapter = OrderAdapter( this, listOf( @@ -26,9 +26,13 @@ class MainActivity : AppCompatActivity(), SteeperView.SteeperHandler { ) ) steeper.setAdapter(adapter) + steeper2.setAdapter(adapter) // Assign Controller - steeper.setController(this) - nextStep.setOnClickListener { steeper.nextStep() } +// steeper.setController(this) + nextStep.setOnClickListener { + steeper.nextStep() + steeper2.nextStep() + } } diff --git a/app/src/main/res/drawable/ic_signal_wifi_0_bar_black_24dp.xml b/app/src/main/res/drawable/ic_signal_wifi_0_bar_black_24dp.xml new file mode 100644 index 0000000..311b2a7 --- /dev/null +++ b/app/src/main/res/drawable/ic_signal_wifi_0_bar_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_signal_wifi_4_bar_black_24dp.xml b/app/src/main/res/drawable/ic_signal_wifi_4_bar_black_24dp.xml new file mode 100644 index 0000000..64342b9 --- /dev/null +++ b/app/src/main/res/drawable/ic_signal_wifi_4_bar_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e610db1..bcd05b0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -15,10 +15,26 @@ tools:context=".MainActivity"> - + + + + diff --git a/app/src/main/res/layout/order_step.xml b/app/src/main/res/layout/order_step.xml index 5f228b9..2e19be2 100644 --- a/app/src/main/res/layout/order_step.xml +++ b/app/src/main/res/layout/order_step.xml @@ -4,8 +4,10 @@ android:layout_height="match_parent" android:layout_marginStart="20dp" android:layout_marginLeft="20dp" + android:paddingStart="20dp" android:orientation="vertical" - android:paddingBottom="4dp"> + android:paddingBottom="4dp" + android:paddingLeft="20dp"> #6200EE #3700B3 #03DAC5 + #ffffff diff --git a/stepeer/build.gradle b/stepeer/build.gradle index 70c6463..59db272 100644 --- a/stepeer/build.gradle +++ b/stepeer/build.gradle @@ -7,7 +7,7 @@ android { buildToolsVersion "29.0.3" defaultConfig { - minSdkVersion 15 + minSdkVersion 16 targetSdkVersion 29 versionCode 1 versionName "1.0" @@ -22,6 +22,13 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } } diff --git a/stepeer/src/main/AndroidManifest.xml b/stepeer/src/main/AndroidManifest.xml index 89826d9..e7d50e1 100644 --- a/stepeer/src/main/AndroidManifest.xml +++ b/stepeer/src/main/AndroidManifest.xml @@ -1,2 +1,2 @@ + package="com.kradwan.stepeer" /> diff --git a/stepeer/src/main/java/com/kradwan/stepeer/model/StepColor.kt b/stepeer/src/main/java/com/kradwan/stepeer/model/StepColor.kt index d65c455..9bc6e65 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/model/StepColor.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/model/StepColor.kt @@ -15,6 +15,18 @@ data class StepColor(var color: Int) { const val COLOR_CHECKED = "checked_color" const val COLOR_UNCHECKED = "unchecked_color" + // For Number Step View + + const val NUM_TEXT_COLOR_CHECKED = "NUM_TEXt_COLOR_CHECKED" + const val NUM_TEXT_COLOR_UNCHECKED = "NUM_TEXt_COLOR_UNCHECKED" + + const val STEP_ICON_CHECKED = "STEP_ICON_CHECKED" + const val STEP_ICON_UNCHECKED = "STEP_ICON_UNCHECKED" + + const val DIVIDER_COLOR_CHECKED = "DIVIDER_COLOR_CHECKED" + const val DIVIDER_COLOR_UNCHECKED = "DIVIDER_COLOR_UNCHECKED" + + val defaultColor = Color.parseColor("#1C8AFF") fun default(): HashMap { diff --git a/stepeer/src/main/java/com/kradwan/stepeer/model/StepDrawable.kt b/stepeer/src/main/java/com/kradwan/stepeer/model/StepDrawable.kt index 3d6fe46..d550058 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/model/StepDrawable.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/model/StepDrawable.kt @@ -8,9 +8,11 @@ import com.kradwan.stepeer.R class StepDrawable(var drawable: Drawable) { companion object { + const val DRAWABLE_CHECKED = "checked_drawable" const val DRAWABLE_UNCHECKED = "unchecked_drawable" + private val defaultIconId = R.drawable.ic_star_black_24dp fun default(context: Context): HashMap { @@ -20,5 +22,11 @@ class StepDrawable(var drawable: Drawable) { Pair(DRAWABLE_UNCHECKED, StepDrawable(drawable)) ) } + + fun fromId(context: Context , id: Int) : StepDrawable{ + return StepDrawable(ContextCompat.getDrawable(context , id)!!) + } } -} \ No newline at end of file +} + +class StepResource(var id: Int) \ No newline at end of file diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt new file mode 100644 index 0000000..e218657 --- /dev/null +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt @@ -0,0 +1,274 @@ +package com.kradwan.stepeer.view + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.os.Parcelable +import android.util.AttributeSet +import android.util.Log +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import androidx.core.view.get +import com.kradwan.stepeer.R +import com.kradwan.stepeer.StepperState +import com.kradwan.stepeer.adapter.StepAdapter +import com.kradwan.stepeer.model.IStep +import com.kradwan.stepeer.model.StepColor +import com.kradwan.stepeer.model.StepDrawable +import com.kradwan.stepeer.model.StepResource +import kotlinx.android.synthetic.main.item_step.view.* +import java.lang.Exception + +/** + * This Class Represent View in XML + * @param context passed to FrameLayout (parent) + * @param attrs passed to FrameLayout (parent) + */ +class NumberStepperView(context: Context, private val attrs: AttributeSet?) : + FrameLayout(context, attrs) { + + // The Root View + private lateinit var view: View + private lateinit var containerSteeper: LinearLayout + // Controller For Finish Step + private var callback: SteeperHandler? = null + + // Index to Current Step to Save State + private var currentStep: Int = 0 + // Colors of `unChecked` and `Checked` state + private var colors = StepColor.default() + private var icons = HashMap() + // Adapter of Stepper + private lateinit var mAdapter: StepAdapter + + init { + initViews() + } + + /** + * Called When App need to reCreate View for Example `rotate` + * To Save State of View + */ + override fun onSaveInstanceState(): Parcelable? { + val superState = super.onSaveInstanceState() + return StepperState(superState, mAdapter.models) + } + + /** + * Called When App need to reCreate Activity After rotate for example etc. + */ + override fun onRestoreInstanceState(state: Parcelable?) { + // cast state to Custom State [ StepperState] + val myState = state as? StepperState + // Pass the State to Parent + super.onRestoreInstanceState(myState?.superSaveState) + // Get The List Of Steps if null [ First Time Open] Assign Empty List + mAdapter.models = myState?.steps ?: listOf() + // Pass Adapter to Build UI + setAdapter(mAdapter, false) + //redraw + } + + @SuppressLint("ResourceType") + private fun initViews() { + // Inflate Root View From XML + view = inflate(context, R.layout.steeper_layout, this) + // Get The Root Element in `steeper_layout` + containerSteeper = view.findViewById(R.id.rootSteeper) + // Check if attrs not null + if (attrs != null) { + // Declare Default Color if The Developer Not Override the + // 'unchecked_color' , 'checked_color'; + // Get the xml style attribute form view + val style = + context.theme.obtainStyledAttributes(attrs, R.styleable.NumberStepperView, 0, 0) + /** + * get First Value [ First from attr file not in XML Order ] + * In our Example the First attr is `check` + */ + + /** + * + + + + + + + + */ + + val numTextColorChecked = + style.getColor(R.styleable.NumberStepperView_num_text_color_checked, -1) + val numTextColorUnChecked = + style.getColor(R.styleable.NumberStepperView_num_text_color_unchecked, -1) + val dividerColorChecked = + style.getColor(R.styleable.NumberStepperView_divider_color_checked, -1) + val dividerColorUnChecked = + style.getColor(R.styleable.NumberStepperView_divider_color_unchecked, -1) + + + val iconChecked = style.getResourceId(R.styleable.SteeperView_checked_icon, -1) + val iconUnChecked = style.getResourceId(R.styleable.SteeperView_unchecked_icon, -1) + + colors[StepColor.NUM_TEXT_COLOR_CHECKED] = + if (numTextColorChecked != -1) StepColor(numTextColorChecked) else StepColor( + Color.parseColor( + "#456333" + ) + ) + + colors[StepColor.NUM_TEXT_COLOR_UNCHECKED] = + if (numTextColorUnChecked != -1) StepColor(numTextColorUnChecked) else StepColor( + Color.parseColor("#456333") + ) + + colors[StepColor.DIVIDER_COLOR_CHECKED] = + if (dividerColorChecked != -1) StepColor(dividerColorChecked) else StepColor( + Color.parseColor( + "#456333" + ) + ) + colors[StepColor.DIVIDER_COLOR_UNCHECKED] = + if (dividerColorUnChecked != -1) StepColor(dividerColorUnChecked) else StepColor( + Color.parseColor("#456333") + ) + + + icons[StepColor.STEP_ICON_UNCHECKED] = StepDrawable.fromId(context ,iconChecked) + icons[StepColor.STEP_ICON_CHECKED] = StepDrawable.fromId( context ,iconUnChecked) + +// val stepIconChecked = style.getDrawable(R.styleable.NumberStepperView_step_icon_checked) +// val stepIconUnChecked = style.getDrawable(R.styleable.NumberStepperView_step_icon_unchecked) +// +// icons[StepColor.STEP_ICON_CHECKED] = +// if (stepIconChecked == null) StepDrawable( +// ContextCompat.getDrawable( +// context, +// R.drawable.ic_check_circle_black_24dp +// )!! +// ) else StepDrawable(stepIconChecked) +// +// icons[StepColor.STEP_ICON_UNCHECKED] = +// if (stepIconChecked == null) StepDrawable( +// ContextCompat.getDrawable( +// context, +// R.drawable.ic_check_circle_black_24dp +// )!! +// ) else StepDrawable(stepIconChecked) + +// icons[StepColor.STEP_ICON_UNCHECKED] = StepDrawable(stepIconChecked!!) + + +// colors[StepColor.COLOR_CHECKED] = +// if (checkColor != -1) StepColor(checkColor) else StepColor(Color.parseColor("#456333")) + +// colors[StepColor.COLOR_UNCHECKED] = +// if (unCheckColor != -1) StepColor(unCheckColor) else StepColor(Color.parseColor("#456333")) +// +// icons[StepDrawable.DRAWABLE_CHECKED] = +// if (checkIcon != -1) StepResource(checkIcon) else StepResource(R.drawable.ic_check_circle_black_24dp) +// +// +// icons[StepDrawable.DRAWABLE_UNCHECKED] = +// if (unCheckIcon != -1) StepResource(unCheckIcon) else StepResource(R.drawable.ic_radio_button_unchecked_black_24dp) + + + } + } + + /** + * Assign Adapter To View To Start Create Step View + * and Show in screen + * @param adapter Any Class Inherited [StepAdapter] + */ + fun setAdapter(adapter: StepAdapter, animated: Boolean = true) { + try { + // because we need assign adapter to global variable + // we need Cast generic Type to IStep + this.mAdapter = adapter as StepAdapter + /** + * We need Remove all View in `containerSteeper` + * because when restore State `onRestoreInstanceState` to duplicate Views + */ + currentStep = 0 + containerSteeper.removeAllViews() + var index = 1 + // Iterate in List of IStep and Build View for Each Model From Adapter + mAdapter.models.forEach { + + // check if step is `Checked` by default + if (it.isChecked()) { + // Increase Indicator of Where Stepper is Stopped + currentStep++ + } + Log.d("DDDD", icons.toString()) + Log.d("DDDD", colors.toString()) + // View of Single Step Without Any Actual Data + val step = SingleNumberStepView(view.context, colors, icons) + // Assign Actual Data to Step View + step.setModel( + it, + adapter.onCreateView(it), + adapter.models.last() == it, + animated, + index++ + ) + // Add Step in Container of Steps + containerSteeper.addView(step) + } + } catch (ex: Exception) { + ex.printStackTrace() + Log.d("DDDD", " EX 22 ${ex.localizedMessage}") + // Handle Any Error and return back to Activity + callback?.onError(ex.localizedMessage) + } + } + + /** + * to Assign Callback Handler to Stepper + */ + fun setController(controller: SteeperHandler) { + callback = controller + } + + /** + * This Method work in Change the UI state from UnChecked to Checked + * @param index of model in list of adapter + */ + private fun selectAsDone(index: Int) { + val stepView = containerSteeper[index] as SingleNumberStepView + stepView.selectAsDone(true) + } + + fun nextStep() { + // Check if Stepper is Finished or not + if (currentStep != containerSteeper.childCount) { + // Change the ui form UnChecked to Checked + selectAsDone(currentStep) + // Change the boolean Variable in list of Adapter + mAdapter.models[currentStep].setChecked(true) + // Increase Indicator to Next Step + currentStep += 1 + } + // Check if we Arrive the End of Stepper + // to notify the callback + if (currentStep == containerSteeper.childCount) { + callback?.onFinish() + } + } + + /** + * Interface represent the handler of actions + * By This Interface you Can Pass Notification to Activity + * When Step reach to last Step + * or if get any error + */ + interface SteeperHandler { + fun onFinish() + + fun onError(msg: String?) + } +} \ No newline at end of file diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt new file mode 100644 index 0000000..be308ad --- /dev/null +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt @@ -0,0 +1,167 @@ +package com.kradwan.stepeer.view + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.util.Log +import android.view.View +import android.view.animation.AnimationUtils +import android.widget.* +import androidx.core.content.ContextCompat +import com.kradwan.stepeer.R +import com.kradwan.stepeer.model.IStep +import com.kradwan.stepeer.model.StepColor +import com.kradwan.stepeer.model.StepDrawable +import com.kradwan.stepeer.model.StepResource + +/** + * This Class Represent Each Step as View + * @param colors Get From Your XML [ 'checked_color' , 'unchecked_color'] + */ + +@SuppressLint("ViewConstructor") +class SingleNumberStepView( + context: Context, + private var colors: HashMap, + private var icons: HashMap +) : + FrameLayout(context) { + + // Setup All Require Views in Layout + private lateinit var view: View + private lateinit var rootView: LinearLayout + private lateinit var stepInfo: LinearLayout + + private lateinit var stepCheckBox: FrameLayout + private lateinit var stepLabel: TextView + // private lateinit var stepCheckBox: CheckBox + private lateinit var stepDivider: View + private lateinit var stepDividerSolid: View + private lateinit var stepContentContainer: LinearLayout + + + /** + * This Code Call When Construct this Class + */ + init { + // Bind Each View With Kotlin Class :) + initViews() + } + + @SuppressLint("ResourceType") + private fun initViews() { + view = inflate(context, R.layout.item_num_step, this) + rootView = view.findViewById(R.id.rootSingleStep) + stepInfo = view.findViewById(R.id.stepInfo) + stepCheckBox = view.findViewById(R.id.stepCheckBok) + stepLabel = view.findViewById(R.id.tvStepLabel) + stepDivider = view.findViewById(R.id.stepDivider) + stepDividerSolid = view.findViewById(R.id.stepDividerSolid) + stepContentContainer = view.findViewById(R.id.stepContentContainer) + + stepCheckBox.setBackgroundResource(R.drawable.ic_radio_button_unchecked_black_24dp) + +// if (icons[StepDrawable.DRAWABLE_UNCHECKED] != null) { +// stepCheckBox.setImageDrawable(icons[StepDrawable.DRAWABLE_UNCHECKED]?.drawable) +// } + /** + * Check if you Override Default Colors + */ + // Check if you override `Checked` state Color + // Change Default Color to your Custom Color + + /** + * Check if you override `unChecked` state Color + * because UnCheck Color is Default and initial State you need Override View Color + */ + stepDivider.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_UNCHECKED]!!.color) + stepDividerSolid.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_UNCHECKED]!!.color) + + /** + * Support sdk 15 and higher than it + * Change Tint of Checkbox to checkBoxUnSelectedColor + */ + + } + + /** + * Here We Bind Actual Data to View + * @param model your Data Class + * @param view Generate View from Adapter + * @param last to hide last Divider + */ + fun setModel(model: T, view: View, last: Boolean, animated: Boolean, label: Int) { + // Set Init State of Checkbox + stepLabel.setTextColor(colors[StepColor.COLOR_UNCHECKED]!!.color) + +// stepCheckBox.setBackgroundResource(R.drawable.ic_radio_button_unchecked_black_24dp) + stepDivider.visibility = if (last) View.GONE else View.VISIBLE +// stepCheckBox.setBackgroundResource(icons[StepDrawable.DRAWABLE_UNCHECKED]!!.id) +// stepCheckBox.background = icons[StepColor.STEP_ICON_CHECKED]!!.drawable + + Log.d("DDDD", " Label ${label}") + stepLabel.text = "$label" + /** + * The Code inside post method call when + * `stepContentContainer` finish Draw in Screen + * to can add View in it Successfully + * and if you need get Width or height of View + */ + stepContentContainer.post { + // Try to Avoid Exception Cases + try { + // Add Custom View in Step Layout + stepContentContainer.addView(view) + } catch (ex: Exception) { + Log.e("STEPPER", ex.localizedMessage!!) + } + + /** + * Check if Step is Done + * to Change Default State to Checked State + */ + if (model.isChecked()) { + // We Pass here if This Model is Last or Not + selectAsDone(animated) + } + + if (last) { + // The LayoutParams form FrameLayout Class + stepDivider.layoutParams = LayoutParams(0, 0) + stepDividerSolid.layoutParams = LayoutParams(0, 0) + } + } + } + + + /** + * This Method Change UI State From UnChecked to + * Checked + */ + fun selectAsDone(animated: Boolean) { + +// stepCheckBox.setBackgroundResource(icons[StepDrawable.DRAWABLE_CHECKED]!!.id) +// stepCheckBox.background = icons[StepColor.STEP_ICON_UNCHECKED]!!.drawable + +// stepCheckBox.background = +// stepCheckBox.background +// stepCheckBox.setBackgroundResource(R.drawable.bg_rounded_fill) + stepLabel.setTextColor(colors[StepColor.COLOR_CHECKED]!!.color) + + /** + * Support sdk 15 and higher than it + * Change Tint of Checkbox to checkBoxSelectedColor + */ + + if (animated) { + stepDividerSolid.setBackgroundColor(colors[StepColor.COLOR_CHECKED]!!.color) + stepDividerSolid.visibility = View.VISIBLE + val move = AnimationUtils.loadAnimation(context, R.anim.move) + stepDividerSolid.animation = move + } else { + stepDividerSolid.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_CHECKED]!!.color) + stepDividerSolid.visibility = View.VISIBLE + } + } + +} \ No newline at end of file diff --git a/stepeer/src/main/res/drawable/bg_rounded_fill.xml b/stepeer/src/main/res/drawable/bg_rounded_fill.xml new file mode 100644 index 0000000..fd0fa6b --- /dev/null +++ b/stepeer/src/main/res/drawable/bg_rounded_fill.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/stepeer/src/main/res/drawable/custom_checkbox.xml b/stepeer/src/main/res/drawable/custom_checkbox.xml index 04ec6ec..6b24c8e 100644 --- a/stepeer/src/main/res/drawable/custom_checkbox.xml +++ b/stepeer/src/main/res/drawable/custom_checkbox.xml @@ -1,9 +1,10 @@ + android:drawable="@drawable/bg_rounded_fill" /> + + android:drawable="@drawable/bg_rounded_fill" /> \ No newline at end of file diff --git a/stepeer/src/main/res/layout/item_num_step.xml b/stepeer/src/main/res/layout/item_num_step.xml new file mode 100644 index 0000000..5ee0862 --- /dev/null +++ b/stepeer/src/main/res/layout/item_num_step.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/stepeer/src/main/res/layout/item_step.xml b/stepeer/src/main/res/layout/item_step.xml index e4c3169..dec42fc 100644 --- a/stepeer/src/main/res/layout/item_step.xml +++ b/stepeer/src/main/res/layout/item_step.xml @@ -18,6 +18,8 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:checked="true" + android:minWidth="35dp" + android:minHeight="35dp" android:contentDescription="@string/step_icon_label" android:enabled="false" android:gravity="center_vertical" /> diff --git a/stepeer/src/main/res/values/attrs.xml b/stepeer/src/main/res/values/attrs.xml index 7f13ea6..9f9caa6 100644 --- a/stepeer/src/main/res/values/attrs.xml +++ b/stepeer/src/main/res/values/attrs.xml @@ -2,10 +2,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file From 02e20a0288003cf42717cab7db52a71c4ff10a4a Mon Sep 17 00:00:00 2001 From: kareemradwan Date: Sat, 4 Jul 2020 20:51:20 +0300 Subject: [PATCH 2/2] add number stepper version 2 --- README.md | 1 + .../com/kareemradwan/stepeer/MainActivity.kt | 2 +- app/src/main/res/layout/activity_main.xml | 9 +- .../com/kradwan/stepeer/model/Constants.kt | 21 ++++ .../com/kradwan/stepeer/model/StepColor.kt | 17 +--- .../{ => numberStepper}/NumberStepperView.kt | 96 ++++++------------- .../SingleNumberStepView.kt | 49 +++------- .../{ => verticalStepper}/SingleStepView.kt | 14 ++- .../view/{ => verticalStepper}/SteeperView.kt | 28 +++--- stepeer/src/main/res/layout/item_num_step.xml | 1 + 10 files changed, 92 insertions(+), 146 deletions(-) create mode 100644 stepeer/src/main/java/com/kradwan/stepeer/model/Constants.kt rename stepeer/src/main/java/com/kradwan/stepeer/view/{ => numberStepper}/NumberStepperView.kt (69%) rename stepeer/src/main/java/com/kradwan/stepeer/view/{ => numberStepper}/SingleNumberStepView.kt (71%) rename stepeer/src/main/java/com/kradwan/stepeer/view/{ => verticalStepper}/SingleStepView.kt (92%) rename stepeer/src/main/java/com/kradwan/stepeer/view/{ => verticalStepper}/SteeperView.kt (90%) diff --git a/README.md b/README.md index 660cae7..bed0ea4 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ You can Register The Activity as Controller to Notifiy When `SteeoerView` Finish - [X] The Developer Can Custmise Color of Step. - [X] Save State of View When Device Rotate. - [X] Custmaize Color for `unChecked` and `Checked` status + - [X] Add Number Stepper View - [ ] Add Animation for `CheckBox` When be Selected diff --git a/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt b/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt index 08aecdd..de84591 100644 --- a/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt +++ b/app/src/main/java/com/kareemradwan/stepeer/MainActivity.kt @@ -4,7 +4,7 @@ import android.annotation.SuppressLint import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.kradwan.stepeer.view.SteeperView +import com.kradwan.stepeer.view.verticalStepper.SteeperView import kotlinx.android.synthetic.main.activity_main.* diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index bcd05b0..af9927a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -15,22 +15,21 @@ tools:context=".MainActivity"> - - { return hashMapOf( - Pair(COLOR_CHECKED, StepColor(defaultColor)), - Pair(COLOR_CHECKED, StepColor(defaultColor)) + Pair(Constants.COLOR_CHECKED, StepColor(defaultColor)), + Pair(Constants.COLOR_CHECKED, StepColor(defaultColor)) ) } } diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/NumberStepperView.kt similarity index 69% rename from stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt rename to stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/NumberStepperView.kt index e218657..28887d6 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/view/NumberStepperView.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/NumberStepperView.kt @@ -1,11 +1,10 @@ -package com.kradwan.stepeer.view +package com.kradwan.stepeer.view.numberStepper import android.annotation.SuppressLint import android.content.Context import android.graphics.Color import android.os.Parcelable import android.util.AttributeSet -import android.util.Log import android.view.View import android.widget.FrameLayout import android.widget.LinearLayout @@ -15,10 +14,9 @@ import com.kradwan.stepeer.R import com.kradwan.stepeer.StepperState import com.kradwan.stepeer.adapter.StepAdapter import com.kradwan.stepeer.model.IStep +import com.kradwan.stepeer.model.Constants import com.kradwan.stepeer.model.StepColor import com.kradwan.stepeer.model.StepDrawable -import com.kradwan.stepeer.model.StepResource -import kotlinx.android.synthetic.main.item_step.view.* import java.lang.Exception /** @@ -88,18 +86,6 @@ class NumberStepperView(context: Context, private val attrs: AttributeSet?) : * get First Value [ First from attr file not in XML Order ] * In our Example the First attr is `check` */ - - /** - * - - - - - - - - */ - val numTextColorChecked = style.getColor(R.styleable.NumberStepperView_num_text_color_checked, -1) val numTextColorUnChecked = @@ -110,71 +96,49 @@ class NumberStepperView(context: Context, private val attrs: AttributeSet?) : style.getColor(R.styleable.NumberStepperView_divider_color_unchecked, -1) - val iconChecked = style.getResourceId(R.styleable.SteeperView_checked_icon, -1) - val iconUnChecked = style.getResourceId(R.styleable.SteeperView_unchecked_icon, -1) - - colors[StepColor.NUM_TEXT_COLOR_CHECKED] = + colors[Constants.NUM_TEXT_COLOR_CHECKED] = if (numTextColorChecked != -1) StepColor(numTextColorChecked) else StepColor( Color.parseColor( "#456333" ) ) - colors[StepColor.NUM_TEXT_COLOR_UNCHECKED] = + colors[Constants.NUM_TEXT_COLOR_UNCHECKED] = if (numTextColorUnChecked != -1) StepColor(numTextColorUnChecked) else StepColor( Color.parseColor("#456333") ) - colors[StepColor.DIVIDER_COLOR_CHECKED] = + colors[Constants.DIVIDER_COLOR_CHECKED] = if (dividerColorChecked != -1) StepColor(dividerColorChecked) else StepColor( Color.parseColor( "#456333" ) ) - colors[StepColor.DIVIDER_COLOR_UNCHECKED] = + colors[Constants.DIVIDER_COLOR_UNCHECKED] = if (dividerColorUnChecked != -1) StepColor(dividerColorUnChecked) else StepColor( Color.parseColor("#456333") ) - icons[StepColor.STEP_ICON_UNCHECKED] = StepDrawable.fromId(context ,iconChecked) - icons[StepColor.STEP_ICON_CHECKED] = StepDrawable.fromId( context ,iconUnChecked) - -// val stepIconChecked = style.getDrawable(R.styleable.NumberStepperView_step_icon_checked) -// val stepIconUnChecked = style.getDrawable(R.styleable.NumberStepperView_step_icon_unchecked) -// -// icons[StepColor.STEP_ICON_CHECKED] = -// if (stepIconChecked == null) StepDrawable( -// ContextCompat.getDrawable( -// context, -// R.drawable.ic_check_circle_black_24dp -// )!! -// ) else StepDrawable(stepIconChecked) -// -// icons[StepColor.STEP_ICON_UNCHECKED] = -// if (stepIconChecked == null) StepDrawable( -// ContextCompat.getDrawable( -// context, -// R.drawable.ic_check_circle_black_24dp -// )!! -// ) else StepDrawable(stepIconChecked) - -// icons[StepColor.STEP_ICON_UNCHECKED] = StepDrawable(stepIconChecked!!) - - -// colors[StepColor.COLOR_CHECKED] = -// if (checkColor != -1) StepColor(checkColor) else StepColor(Color.parseColor("#456333")) - -// colors[StepColor.COLOR_UNCHECKED] = -// if (unCheckColor != -1) StepColor(unCheckColor) else StepColor(Color.parseColor("#456333")) + val stepIconChecked = style.getDrawable(R.styleable.NumberStepperView_step_icon_checked) + val stepIconUnChecked = + style.getDrawable(R.styleable.NumberStepperView_step_icon_unchecked) // -// icons[StepDrawable.DRAWABLE_CHECKED] = -// if (checkIcon != -1) StepResource(checkIcon) else StepResource(R.drawable.ic_check_circle_black_24dp) -// -// -// icons[StepDrawable.DRAWABLE_UNCHECKED] = -// if (unCheckIcon != -1) StepResource(unCheckIcon) else StepResource(R.drawable.ic_radio_button_unchecked_black_24dp) - + icons[Constants.STEP_ICON_CHECKED] = + if (stepIconChecked == null) StepDrawable( + ContextCompat.getDrawable( + context, + R.drawable.bg_rounded_fill + )!! + ) else StepDrawable(stepIconChecked) + + icons[Constants.STEP_ICON_UNCHECKED] = + if (stepIconUnChecked == null) StepDrawable( + ContextCompat.getDrawable( + context, + R.drawable.ic_radio_button_unchecked_black_24dp + )!! + ) else StepDrawable(stepIconUnChecked) } } @@ -204,10 +168,13 @@ class NumberStepperView(context: Context, private val attrs: AttributeSet?) : // Increase Indicator of Where Stepper is Stopped currentStep++ } - Log.d("DDDD", icons.toString()) - Log.d("DDDD", colors.toString()) // View of Single Step Without Any Actual Data - val step = SingleNumberStepView(view.context, colors, icons) + val step = + SingleNumberStepView( + view.context, + colors, + icons + ) // Assign Actual Data to Step View step.setModel( it, @@ -220,9 +187,6 @@ class NumberStepperView(context: Context, private val attrs: AttributeSet?) : containerSteeper.addView(step) } } catch (ex: Exception) { - ex.printStackTrace() - Log.d("DDDD", " EX 22 ${ex.localizedMessage}") - // Handle Any Error and return back to Activity callback?.onError(ex.localizedMessage) } } diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/SingleNumberStepView.kt similarity index 71% rename from stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt rename to stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/SingleNumberStepView.kt index be308ad..7b936ae 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/view/SingleNumberStepView.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/numberStepper/SingleNumberStepView.kt @@ -1,18 +1,16 @@ -package com.kradwan.stepeer.view +package com.kradwan.stepeer.view.numberStepper import android.annotation.SuppressLint import android.content.Context -import android.graphics.Color import android.util.Log import android.view.View import android.view.animation.AnimationUtils import android.widget.* -import androidx.core.content.ContextCompat import com.kradwan.stepeer.R +import com.kradwan.stepeer.model.Constants import com.kradwan.stepeer.model.IStep import com.kradwan.stepeer.model.StepColor import com.kradwan.stepeer.model.StepDrawable -import com.kradwan.stepeer.model.StepResource /** * This Class Represent Each Step as View @@ -20,7 +18,7 @@ import com.kradwan.stepeer.model.StepResource */ @SuppressLint("ViewConstructor") -class SingleNumberStepView( +internal class SingleNumberStepView( context: Context, private var colors: HashMap, private var icons: HashMap @@ -59,28 +57,11 @@ class SingleNumberStepView( stepDividerSolid = view.findViewById(R.id.stepDividerSolid) stepContentContainer = view.findViewById(R.id.stepContentContainer) - stepCheckBox.setBackgroundResource(R.drawable.ic_radio_button_unchecked_black_24dp) + // Init Default Values OF UI COLORS and IMAGES + stepCheckBox.background = (icons[Constants.STEP_ICON_UNCHECKED]!!.drawable) + stepDivider.setBackgroundColor(colors[Constants.DIVIDER_COLOR_UNCHECKED]!!.color) + stepDividerSolid.setBackgroundColor(colors[Constants.DIVIDER_COLOR_UNCHECKED]!!.color) -// if (icons[StepDrawable.DRAWABLE_UNCHECKED] != null) { -// stepCheckBox.setImageDrawable(icons[StepDrawable.DRAWABLE_UNCHECKED]?.drawable) -// } - /** - * Check if you Override Default Colors - */ - // Check if you override `Checked` state Color - // Change Default Color to your Custom Color - - /** - * Check if you override `unChecked` state Color - * because UnCheck Color is Default and initial State you need Override View Color - */ - stepDivider.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_UNCHECKED]!!.color) - stepDividerSolid.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_UNCHECKED]!!.color) - - /** - * Support sdk 15 and higher than it - * Change Tint of Checkbox to checkBoxUnSelectedColor - */ } @@ -92,7 +73,7 @@ class SingleNumberStepView( */ fun setModel(model: T, view: View, last: Boolean, animated: Boolean, label: Int) { // Set Init State of Checkbox - stepLabel.setTextColor(colors[StepColor.COLOR_UNCHECKED]!!.color) +// stepLabel.setTextColor(colors[StepColor.COLOR_UNCHECKED]!!.color) // stepCheckBox.setBackgroundResource(R.drawable.ic_radio_button_unchecked_black_24dp) stepDivider.visibility = if (last) View.GONE else View.VISIBLE @@ -139,14 +120,8 @@ class SingleNumberStepView( * Checked */ fun selectAsDone(animated: Boolean) { - -// stepCheckBox.setBackgroundResource(icons[StepDrawable.DRAWABLE_CHECKED]!!.id) -// stepCheckBox.background = icons[StepColor.STEP_ICON_UNCHECKED]!!.drawable - -// stepCheckBox.background = -// stepCheckBox.background -// stepCheckBox.setBackgroundResource(R.drawable.bg_rounded_fill) - stepLabel.setTextColor(colors[StepColor.COLOR_CHECKED]!!.color) + stepCheckBox.background = icons[Constants.STEP_ICON_CHECKED]!!.drawable + stepLabel.setTextColor(colors[Constants.NUM_TEXT_COLOR_CHECKED]!!.color) /** * Support sdk 15 and higher than it @@ -154,12 +129,12 @@ class SingleNumberStepView( */ if (animated) { - stepDividerSolid.setBackgroundColor(colors[StepColor.COLOR_CHECKED]!!.color) + stepDividerSolid.setBackgroundColor(colors[Constants.DIVIDER_COLOR_CHECKED]!!.color) stepDividerSolid.visibility = View.VISIBLE val move = AnimationUtils.loadAnimation(context, R.anim.move) stepDividerSolid.animation = move } else { - stepDividerSolid.setBackgroundColor(colors[StepColor.DIVIDER_COLOR_CHECKED]!!.color) + stepDividerSolid.setBackgroundColor(colors[Constants.DIVIDER_COLOR_CHECKED]!!.color) stepDividerSolid.visibility = View.VISIBLE } } diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/SingleStepView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SingleStepView.kt similarity index 92% rename from stepeer/src/main/java/com/kradwan/stepeer/view/SingleStepView.kt rename to stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SingleStepView.kt index 5b48f9c..9c1bd8d 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/view/SingleStepView.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SingleStepView.kt @@ -1,4 +1,4 @@ -package com.kradwan.stepeer.view +package com.kradwan.stepeer.view.verticalStepper import android.annotation.SuppressLint import android.content.Context @@ -10,9 +10,7 @@ import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import com.kradwan.stepeer.R -import com.kradwan.stepeer.model.IStep -import com.kradwan.stepeer.model.StepColor -import com.kradwan.stepeer.model.StepDrawable +import com.kradwan.stepeer.model.* /** * This Class Represent Each Step as View @@ -20,7 +18,7 @@ import com.kradwan.stepeer.model.StepDrawable */ @SuppressLint("ViewConstructor") -class SingleStepView( +internal class SingleStepView( context: Context, private var colors: HashMap, private var icons: HashMap @@ -67,13 +65,13 @@ class SingleStepView( */ // Check if you override `Checked` state Color // Change Default Color to your Custom Color - checkBoxSelectedColor = colors[StepColor.COLOR_CHECKED]!! + checkBoxSelectedColor = colors[Constants.COLOR_CHECKED]!! /** * Check if you override `unChecked` state Color * because UnCheck Color is Default and initial State you need Override View Color */ - checkBoxUnSelectedColor = colors[StepColor.COLOR_UNCHECKED]!! + checkBoxUnSelectedColor = colors[Constants.COLOR_UNCHECKED]!! stepDivider.setBackgroundColor(checkBoxUnSelectedColor.color) stepDividerSolid.setBackgroundColor(checkBoxUnSelectedColor.color) @@ -140,7 +138,7 @@ class SingleStepView( */ if (animated) { - stepDividerSolid.setBackgroundColor(colors[StepColor.COLOR_CHECKED]!!.color) + stepDividerSolid.setBackgroundColor(colors[Constants.COLOR_CHECKED]!!.color) stepDividerSolid.visibility = View.VISIBLE val move = AnimationUtils.loadAnimation(context, R.anim.move) stepDividerSolid.animation = move diff --git a/stepeer/src/main/java/com/kradwan/stepeer/view/SteeperView.kt b/stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SteeperView.kt similarity index 90% rename from stepeer/src/main/java/com/kradwan/stepeer/view/SteeperView.kt rename to stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SteeperView.kt index f478967..3c48260 100644 --- a/stepeer/src/main/java/com/kradwan/stepeer/view/SteeperView.kt +++ b/stepeer/src/main/java/com/kradwan/stepeer/view/verticalStepper/SteeperView.kt @@ -1,11 +1,9 @@ -package com.kradwan.stepeer.view +package com.kradwan.stepeer.view.verticalStepper import android.annotation.SuppressLint import android.content.Context -import android.graphics.Color import android.os.Parcelable import android.util.AttributeSet -import android.util.Log import android.view.View import android.widget.FrameLayout import android.widget.LinearLayout @@ -13,10 +11,7 @@ import androidx.core.view.get import com.kradwan.stepeer.R import com.kradwan.stepeer.StepperState import com.kradwan.stepeer.adapter.StepAdapter -import com.kradwan.stepeer.model.IStep -import com.kradwan.stepeer.model.StepColor -import com.kradwan.stepeer.model.StepDrawable -import kotlinx.android.synthetic.main.item_step.view.* +import com.kradwan.stepeer.model.* import java.lang.Exception /** @@ -24,7 +19,7 @@ import java.lang.Exception * @param context passed to FrameLayout (parent) * @param attrs passed to FrameLayout (parent) */ -class SteeperView(context: Context, private val attrs: AttributeSet?) : + class SteeperView(context: Context, private val attrs: AttributeSet?) : FrameLayout(context, attrs) { // The Root View @@ -65,7 +60,7 @@ class SteeperView(context: Context, private val attrs: AttributeSet?) : // Get The List Of Steps if null [ First Time Open] Assign Empty List mAdapter.models = myState?.steps ?: listOf() // Pass Adapter to Build UI - setAdapter(mAdapter , false) + setAdapter(mAdapter, false) //redraw } @@ -91,9 +86,9 @@ class SteeperView(context: Context, private val attrs: AttributeSet?) : val unCheckIcon = style.getDrawable(R.styleable.SteeperView_unchecked_icon) - colors[StepColor.COLOR_CHECKED] = + colors[Constants.COLOR_CHECKED] = if (checkColor != -1) StepColor(checkColor) else StepColor(StepColor.defaultColor) - colors[StepColor.COLOR_UNCHECKED] = + colors[Constants.COLOR_UNCHECKED] = if (unCheckColor != -1) StepColor(unCheckColor) else StepColor(StepColor.defaultColor) @@ -117,7 +112,7 @@ class SteeperView(context: Context, private val attrs: AttributeSet?) : * and Show in screen * @param adapter Any Class Inherited [StepAdapter] */ - fun setAdapter(adapter: StepAdapter , animated: Boolean = true) { + fun setAdapter(adapter: StepAdapter, animated: Boolean = true) { try { // because we need assign adapter to global variable // we need Cast generic Type to IStep @@ -136,9 +131,14 @@ class SteeperView(context: Context, private val attrs: AttributeSet?) : currentStep++ } // View of Single Step Without Any Actual Data - val step = SingleStepView(view.context, colors, icons) + val step = + SingleStepView( + view.context, + colors, + icons + ) // Assign Actual Data to Step View - step.setModel(it, adapter.onCreateView(it), adapter.models.last() == it , animated) + step.setModel(it, adapter.onCreateView(it), adapter.models.last() == it, animated) // Add Step in Container of Steps containerSteeper.addView(step) } diff --git a/stepeer/src/main/res/layout/item_num_step.xml b/stepeer/src/main/res/layout/item_num_step.xml index 5ee0862..7c65039 100644 --- a/stepeer/src/main/res/layout/item_num_step.xml +++ b/stepeer/src/main/res/layout/item_num_step.xml @@ -29,6 +29,7 @@ android:layout_gravity="center" android:gravity="center" android:minWidth="35dp" + android:textStyle="bold" android:minHeight="35dp" tools:text="2" android:textAlignment="center" />