diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..4df02b2 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsKotlinAndroid) +} + + +android { + namespace = "com.example.accountbook" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.accountbook" + minSdk = 28 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/accountbook/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/accountbook/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..452a8ab --- /dev/null +++ b/app/src/androidTest/java/com/example/accountbook/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.accountbook + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.accountbook", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..55d27ca --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/FirstFragment.kt b/app/src/main/java/com/example/accountbook/FirstFragment.kt new file mode 100644 index 0000000..7e3f24e --- /dev/null +++ b/app/src/main/java/com/example/accountbook/FirstFragment.kt @@ -0,0 +1,44 @@ +package com.example.accountbook + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import com.example.accountbook.databinding.FragmentFirstBinding + +/** + * A simple [Fragment] subclass as the default destination in the navigation. + */ +class FirstFragment : Fragment() { + + private var _binding: FragmentFirstBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + + _binding = FragmentFirstBinding.inflate(inflater, container, false) + return binding.root + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + +// binding.buttonFirst.setOnClickListener { +// findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) +// } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/MainActivity.kt b/app/src/main/java/com/example/accountbook/MainActivity.kt new file mode 100644 index 0000000..d179de7 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/MainActivity.kt @@ -0,0 +1,63 @@ +package com.example.accountbook + +import android.content.Intent +import android.os.Bundle +import com.google.android.material.snackbar.Snackbar +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.findNavController +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.navigateUp +import androidx.navigation.ui.setupActionBarWithNavController +import android.view.Menu +import android.view.MenuItem +import com.example.accountbook.activity.ActivityCalendar +import com.example.accountbook.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + + private lateinit var appBarConfiguration: AppBarConfiguration + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + setSupportActionBar(binding.toolbar) + + val navController = findNavController(R.id.nav_host_fragment_content_main) + appBarConfiguration = AppBarConfiguration(navController.graph) + setupActionBarWithNavController(navController, appBarConfiguration) + + binding.fab.setOnClickListener { view -> + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null) + .setAnchorView(R.id.fab).show() + } + + startActivity(Intent(applicationContext, ActivityCalendar::class.java)) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.menu_main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + return when (item.itemId) { + R.id.action_settings -> true + else -> super.onOptionsItemSelected(item) + } + } + + override fun onSupportNavigateUp(): Boolean { + val navController = findNavController(R.id.nav_host_fragment_content_main) + return navController.navigateUp(appBarConfiguration) + || super.onSupportNavigateUp() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/SecondFragment.kt b/app/src/main/java/com/example/accountbook/SecondFragment.kt new file mode 100644 index 0000000..4dbd3d7 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/SecondFragment.kt @@ -0,0 +1,44 @@ +package com.example.accountbook + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import com.example.accountbook.databinding.FragmentSecondBinding + +/** + * A simple [Fragment] subclass as the second destination in the navigation. + */ +class SecondFragment : Fragment() { + + private var _binding: FragmentSecondBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + + _binding = FragmentSecondBinding.inflate(inflater, container, false) + return binding.root + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.buttonSecond.setOnClickListener { + findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/activity/ActivityCalendar.kt b/app/src/main/java/com/example/accountbook/activity/ActivityCalendar.kt new file mode 100644 index 0000000..efa49d5 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/activity/ActivityCalendar.kt @@ -0,0 +1,49 @@ +package com.example.accountbook.activity + +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import com.example.accountbook.R +import com.example.accountbook.calendar.CalendarAdapter +import com.example.accountbook.calendar.CalendarBean +import com.example.accountbook.calendar.CalendarDateView +import com.example.accountbook.databinding.ActivityCalendarBinding +import com.example.accountbook.fragment.FragmentCalendar +import com.example.accountbook.fragment.FragmentTable + +class ActivityCalendar: AppCompatActivity(){ + private lateinit var bind: ActivityCalendarBinding + val fragmentCalendar: FragmentCalendar by lazy { FragmentCalendar() } + val fragmentTable: FragmentTable by lazy { FragmentTable() } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // binding view + bind = ActivityCalendarBinding.inflate(this.layoutInflater) + // setting content view + setContentView(bind.root) + bind.chkShift.setOnClickListener() { _ -> + // 화면 전환 + supportFragmentManager.beginTransaction() + .replace( + R.id.fragment_box, + if (bind.chkShift.isChecked) fragmentTable else fragmentCalendar + ) + .commit() + } + + supportFragmentManager.beginTransaction() + .replace( + R.id.fragment_box, fragmentCalendar + ) + .commit() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/activity/ActivityWriter.kt b/app/src/main/java/com/example/accountbook/activity/ActivityWriter.kt new file mode 100644 index 0000000..8df5617 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/activity/ActivityWriter.kt @@ -0,0 +1,20 @@ +package com.example.accountbook.activity + +import android.os.Bundle +import android.widget.ArrayAdapter +import androidx.appcompat.app.AppCompatActivity +import com.example.accountbook.R +import com.example.accountbook.databinding.ActivityWriterBinding + +class ActivityWriter: AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val bind = ActivityWriterBinding.inflate(this.layoutInflater) + setContentView(bind.root) + val adapter = ArrayAdapter(this, R.layout.item_spinner_writer_category, arrayListOf("기타","식비","통신비","급여")) + bind.spinnerWriteCategory.adapter = adapter + bind.btnWriteClose.setOnClickListener{ + this.finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/adapter/AdapterTable.kt b/app/src/main/java/com/example/accountbook/adapter/AdapterTable.kt new file mode 100644 index 0000000..c2a3042 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/adapter/AdapterTable.kt @@ -0,0 +1,27 @@ +package com.example.accountbook.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import com.example.accountbook.R +import com.example.accountbook.databinding.ItemTableBinding + +class AdapterTable(context: Context, private val data: Array): + ArrayAdapter(context, R.layout.item_table, data) { + private lateinit var bind:ItemTableBinding + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var rView: View? = convertView + if (rView == null) { + // 최초 생성 + bind = ItemTableBinding.inflate(LayoutInflater.from(parent.context), parent, false) + } + bind.textDate.text = "10/25" + bind.textAmount.text = "10,000" + bind.textMemo.text = "커피" + bind.textCategory.text = "기타" + return bind.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarAdapter.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarAdapter.kt new file mode 100644 index 0000000..69212a3 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarAdapter.kt @@ -0,0 +1,10 @@ +package com.example.accountbook.calendar + +import android.view.View +import android.view.ViewGroup + + +interface CalendarAdapter { + fun getView(convertView: View?, parentView: ViewGroup?, bean: CalendarBean?): View + fun hasChildView() : Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarBean.java b/app/src/main/java/com/example/accountbook/calendar/CalendarBean.java new file mode 100644 index 0000000..f231d78 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarBean.java @@ -0,0 +1,54 @@ +package com.example.accountbook.calendar; + +public class CalendarBean { + public int year; + public int month; + public int day; + public int week; + + //-1,0,1 + public int monthFlag; + + //显示 + + public CalendarBean(int year, int month, int day) { + this.year = year; + this.month = month; + this.day = day; + } + + public String getDisplayWeek(){ + String s=""; + switch(week){ + case 1: + s="星期日"; + break; + case 2: + s="星期一"; + break; + case 3: + s="星期二"; + break; + case 4: + s="星期三"; + break; + case 5: + s="星期四"; + break; + case 6: + s="星期五"; + break; + case 7: + s="星期六"; + break; + + } + return s ; + } + + @Override + public String toString() { + String s=year+"/"+month+"/"+day; + return s; + } +} diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarDateView.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarDateView.kt new file mode 100644 index 0000000..670caa4 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarDateView.kt @@ -0,0 +1,138 @@ +package com.example.accountbook.calendar + +import android.content.Context +import android.util.AttributeSet +import android.util.Log +import android.view.View +import android.view.ViewGroup +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.example.accountbook.R +import com.example.accountbook.calendar.CalendarFactory.getMonthOfDayList +import java.util.Date +import java.util.LinkedList + + +class CalendarDateView(context: Context, attrs: AttributeSet?): + ViewPager(context, attrs), CalendarTopView { + var views = HashMap() + private var mCaledarLayoutChangeListener: CalendarTopViewChangeListener? = null + private var onItemClickListener: CalendarView.OnItemClickListener? = null + private val cache: LinkedList = LinkedList() + private val MAXCOUNT = 6 + private var row = 6 + private var mAdapter: CalendarAdapter? = null + override var itemHeight = 0 + private set + + + fun setAdapter(adapter: CalendarAdapter?) { + mAdapter = adapter + initialize() + initData() + } + + fun setOnItemClickListener(onItemClickListener: CalendarView.OnItemClickListener?) { + this.onItemClickListener = onItemClickListener + } + + init { + val a = context.obtainStyledAttributes(attrs, R.styleable.CalendarDateView) + row = a.getInteger(R.styleable.CalendarDateView_cbd_calendar_row, 6) + a.recycle() + + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) +// var calendarHeight = 0 + if (adapter != null) { + (getChildAt(0) as CalendarView)?.let { +// calendarHeight = it.measuredHeight + itemHeight = it.itemHeight + } + } + setMeasuredDimension( + widthMeasureSpec, + MeasureSpec.makeMeasureSpec(heightMeasureSpec, MeasureSpec.EXACTLY) + ) + } + + private fun initialize() { + val dateArr: IntArray = CalendarUtil.getYMD(Date()) + setAdapter(object : PagerAdapter() { + override fun getCount(): Int { +// Log.i(this@CalendarDateView::class.java.simpleName, "container >>> setAdapter getCount") + return Int.MAX_VALUE + } + + override fun isViewFromObject(view: View, `object`: Any): Boolean { + return view === `object` + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + + Log.i(this@CalendarDateView::class.java.simpleName, "container >>> ${container} position >> ${position}") + val view: CalendarView = if (!cache.isEmpty()) { + cache.removeFirst()!! + } else { + CalendarView(container.context, row) + } + view.setOnItemClickListener(onItemClickListener) + view.setAdapter(mAdapter) + + container.addView(view) + views[position] = view + view.apply { + setData( + getMonthOfDayList( + dateArr[0], + dateArr[1] + position - Int.MAX_VALUE / 2 + ), position == Int.MAX_VALUE / 2 + ) + } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { + container.removeView(`object` as View) + cache.addLast(`object` as CalendarView) + views.remove(position) + } + }) + addOnPageChangeListener(object: SimpleOnPageChangeListener() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + onItemClickListener?.let{ + views[position]?.let {v -> + val obs: Array = v.select + it.onItemClick( + obs[0] as View, + obs[1] as Int, + obs[2] as CalendarBean + ) + } + } + mCaledarLayoutChangeListener?.onLayoutChange(this@CalendarDateView) + } + }) + } + + private fun initData() { + setCurrentItem(Int.MAX_VALUE / 2, false) + adapter!!.notifyDataSetChanged() + } + + override val currentSelectPositon: IntArray + get() { + var view = views[currentItem] + if (view == null) { + view = getChildAt(0) as? CalendarView + } + return view?.getSelectPosition() ?: IntArray(4) + } + + override fun setCalendarTopViewChangeListener(listener: CalendarTopViewChangeListener?) { + mCaledarLayoutChangeListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarFactory.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarFactory.kt new file mode 100644 index 0000000..ac15d4c --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarFactory.kt @@ -0,0 +1,66 @@ +package com.example.accountbook.calendar + +import android.util.Log +import com.example.accountbook.calendar.CalendarUtil.getDayOfWeek +import java.util.Calendar + + +object CalendarFactory { + private val cache = HashMap>() + + fun getMonthOfDayList(y: Int, m: Int): List { + Log.e("TIME CHECK" , "fun getMonthOfDayList Start") + val key = y.toString() + "" + m + if (cache.containsKey(key)) { + val list = cache[key] + if (list == null) { + cache.remove(key) + } else { + return list + } + } + val list: MutableList = ArrayList() + cache[key] = list + + val fweek: Int = getDayOfWeek(y, m, 1) + val total: Int = CalendarUtil.getDayOfMaonth(y, m) + + for (i in fweek - 1 downTo 1) { + val bean = getCalendarBean(y, m, 1 - i) + bean.monthFlag = -1 + list.add(bean) + } + + for (i in 0 until total) { + val bean = getCalendarBean(y, m, i + 1) + list.add(bean) + } + + for (i in 0 until 42 - (fweek - 1) - total) { + val bean = getCalendarBean(y, m, total + i + 1) + bean.monthFlag = 1 + list.add(bean) + } + Log.e("TIME CHECK" , "fun getMonthOfDayList END") + return list + } + + fun getCalendarBean(year: Int, month: Int, day: Int): CalendarBean { + + val calendar: Calendar = Calendar.getInstance() + .apply { this.set(year, month - 1, day) } + val year = calendar.get(Calendar.YEAR) + val month = calendar.get(Calendar.MONTH) + 1 + val day = calendar.get(Calendar.DATE) + val bean = CalendarBean(year, month, day) + bean.week = CalendarUtil.getDayOfWeek(year, month, day) +// val chinaDate: Array = ChinaDate.getChinaDate(year, month, day) +// bean.chinaMonth = chinaDate[0] +// bean.chinaDay = chinaDate[1] + return bean + } + + @JvmStatic + fun main(args: Array) { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarLayout.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarLayout.kt new file mode 100644 index 0000000..52b4b50 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarLayout.kt @@ -0,0 +1,318 @@ +package com.example.accountbook.calendar + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import android.util.Log +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.View +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.view.animation.Interpolator +import android.widget.AbsListView +import android.widget.FrameLayout +import android.widget.ListView +import androidx.core.view.VelocityTrackerCompat +import androidx.core.view.ViewCompat +import androidx.core.widget.ScrollerCompat +import kotlin.math.abs + + +class CalendarLayout : FrameLayout { + private var view1: View? = null +// private var view2: ViewGroup? = null + private var mTopView: CalendarTopView? = null + var type = TYPE_OPEN + + private var isSilde = false + private var topHeigth = 0 + private var itemHeight = 0 + private var bottomViewTopHeight = 0 + private var maxDistance = 0 + private var mScroller: ScrollerCompat? = null + private var mMaxVelocity = 0f + private var mMinVelocity = 0f + private var activitPotionerId = 0 + + constructor(context: Context?) : super(context!!) { + init() + } + + constructor(context: Context?, attrs: AttributeSet?) : super( + context!!, attrs + ) { + init() + } + + override fun onFinishInflate() { + super.onFinishInflate() + val viewPager: CalendarTopView = getChildAt(0) as CalendarTopView + mTopView = viewPager + view1 = viewPager as View +// view2 = getChildAt(1) as ViewGroup + mTopView?.setCalendarTopViewChangeListener(object: CalendarTopViewChangeListener { + override fun onLayoutChange(topView: CalendarTopView) { + requestLayout() + } + }) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + itemHeight = mTopView?.itemHeight ?: 0 + topHeigth = view1?.measuredHeight ?: 0 + maxDistance = topHeigth - itemHeight + when (type) { + TYPE_FOLD -> bottomViewTopHeight = itemHeight + TYPE_OPEN -> bottomViewTopHeight = topHeigth + } +// view2!!.measure( +// widthMeasureSpec, +// MeasureSpec.makeMeasureSpec( +// MeasureSpec.getSize(heightMeasureSpec) - (mTopView?.itemHeight ?: 0), +// MeasureSpec.EXACTLY +// ) +// ) + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) +// view2!!.offsetTopAndBottom(bottomViewTopHeight) + val selectRct = selectRect + if (type == TYPE_FOLD) { + view1!!.offsetTopAndBottom(-selectRct[1]) + } + } + + private fun init() { + val vc = ViewConfiguration.get(context) + mMaxVelocity = vc.scaledMaximumFlingVelocity.toFloat() + mMinVelocity = vc.scaledMinimumFlingVelocity.toFloat() + mScroller = ScrollerCompat.create(context, sInterpolator) + } + + var oy = 0f + var ox = 0f + var isClickBtottomView = false +// override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { +//// mViewDragHelper.shouldInterceptTouchEvent(ev); +// var isflag = false +// when (ev.action) { +// MotionEvent.ACTION_DOWN -> { +// oy = ev.y +// ox = ev.x +// isClickBtottomView = isClickView(view2, ev) +// cancel() +// activitPotionerId = ev.getPointerId(0) +// val top = view2!!.top +// type = if (top < topHeigth) { +// TYPE_FOLD +// } else { +// TYPE_OPEN +// } +// } +// +// MotionEvent.ACTION_MOVE -> { +// val y = ev.y +// val x = ev.x +// val xdiff = x - ox +// val ydiff = y - oy +// if (abs(ydiff.toDouble()) > 5 && abs(ydiff.toDouble()) > abs(xdiff.toDouble())) { +// isflag = true +// if (isClickBtottomView) { +// val isScroll = isScroll(view2) +// if (ydiff > 0) { +// //向下 +// if (type == TYPE_OPEN) { +// return super.onInterceptTouchEvent(ev) +// } else { +// if (isScroll) { +// return super.onInterceptTouchEvent(ev) +// } +// } +// } else { +// //向上 +// if (type == TYPE_FOLD) { +// return super.onInterceptTouchEvent(ev) +// } else { +// if (isScroll) { +// return super.onInterceptTouchEvent(ev) +// } +// } +// } +// } +// } +// ox = x +// oy = y +// } +// +// MotionEvent.ACTION_UP -> {} +// } +// return isSilde || isflag || super.onInterceptTouchEvent(ev) +// } + + private fun isScroll(view2: ViewGroup?): Boolean { + val fistChildView = view2!!.getChildAt(0) ?: return false + if (view2 is ListView) { + val list = view2 as AbsListView + if (fistChildView.top != 0) { + return true + } else { + if (list.getPositionForView(fistChildView) != 0) { + return true + } + } + } + return false + } + + fun isClickView(view: View?, ev: MotionEvent): Boolean { + val rect = Rect() + view!!.getHitRect(rect) + val isClick = rect.contains(ev.x.toInt(), ev.y.toInt()) + Log.d( + TAG, + "isClickView() called with: isClick = [$isClick]" + ) + return isClick + } + +// override fun onTouchEvent(event: MotionEvent): Boolean { +// processTouchEvent(event) +// return true +// } + + private var mVelocityTracker: VelocityTracker? = null +// fun processTouchEvent(event: MotionEvent) { +// if (mVelocityTracker == null) { +// mVelocityTracker = VelocityTracker.obtain() +// } +// mVelocityTracker!!.addMovement(event) +// when (event.action) { +// MotionEvent.ACTION_DOWN -> {} +// MotionEvent.ACTION_MOVE -> { +// if (isSilde) { +// return +// } +// val cy = event.y +// val dy = (cy - oy).toInt() +// if (dy == 0) { +// return +// } +// oy = cy +// move(dy) +// } +// +// MotionEvent.ACTION_UP -> { +// if (isSilde) { +// cancel() +// return +// } +// +// //判断速度 +// val pointerId = activitPotionerId +// mVelocityTracker!!.computeCurrentVelocity(1000, mMaxVelocity) +// val crrentV = VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId) +// if (abs(crrentV.toDouble()) > 2000) { +// if (crrentV > 0) { +// open() +// } else { +// flod() +// } +// cancel() +// return +// } +// val top = view2!!.top - topHeigth +// val maxd = maxDistance +// if (abs(top.toDouble()) < maxd / 2) { +// open() +// } else { +// flod() +// } +// cancel() +// } +// +// MotionEvent.ACTION_CANCEL -> cancel() +// } +// } + +// fun open() { +// startScroll(view2!!.top, topHeigth) +// } +// +// fun flod() { +// startScroll(view2!!.top, topHeigth - maxDistance) +// } + + private val selectRect: IntArray + get() = mTopView?.currentSelectPositon ?: intArrayOf() + +// private fun move(dy: Int) { +// val selectRect = selectRect +// val itemHeight: Int = mTopView?.itemHeight ?: 0 +// val dy1 = getAreaValue(view1!!.top, dy, -selectRect[1], 0) +// val dy2 = getAreaValue(view2!!.top - topHeigth, dy, -(topHeigth - itemHeight), 0) +// if (dy1 != 0) { +// ViewCompat.offsetTopAndBottom(view1!!, dy1) +// } +// if (dy2 != 0) { +// ViewCompat.offsetTopAndBottom(view2!!, dy2) +// } +// } + + private fun getAreaValue(top: Int, dy: Int, minValue: Int, maxValue: Int): Int { + if (top + dy < minValue) { + return minValue - top + } + return if (top + dy > maxValue) { + maxValue - top + } else dy + } + + private fun startScroll(starty: Int, endY: Int) { + val distance = (endY - starty).toFloat() + val t = distance / maxDistance * 600 + mScroller!!.startScroll(0, 0, 0, endY - starty, abs(t.toDouble()).toInt()) + postInvalidate() + } + + var oldY = 0 +// override fun computeScroll() { +// super.computeScroll() +// bottomViewTopHeight = view2!!.top +// if (mScroller!!.computeScrollOffset()) { +// isSilde = true +// val cy = mScroller!!.currY +// val dy = cy - oldY +// move(dy) +// oldY = cy +// postInvalidate() +// } else { +// oldY = 0 +// isSilde = false +// } +// } + + fun cancel() { + if (mVelocityTracker != null) { + mVelocityTracker!!.recycle() + mVelocityTracker = null + } + } + + companion object { + private const val TAG = "CalendarLayout" + + const val TYPE_OPEN = 0 + + const val TYPE_FOLD = 1 + private val sInterpolator: Interpolator = object: Interpolator { + override fun getInterpolation(t: Float): Float { + var t = t + t -= 1.0f + return t * t * t * t * t + 1.0f + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarTopView.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarTopView.kt new file mode 100644 index 0000000..f525a90 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarTopView.kt @@ -0,0 +1,9 @@ +package com.example.accountbook.calendar + + +interface CalendarTopView { + val currentSelectPositon: IntArray? + val itemHeight: Int + + fun setCalendarTopViewChangeListener(listener: CalendarTopViewChangeListener?) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarTopViewChangeListener.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarTopViewChangeListener.kt new file mode 100644 index 0000000..3a8ae60 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarTopViewChangeListener.kt @@ -0,0 +1,7 @@ +package com.example.accountbook.calendar + +interface CalendarTopViewChangeListener { + fun onLayoutChange(topView: CalendarTopView) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarUtil.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarUtil.kt new file mode 100644 index 0000000..6bf73a0 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarUtil.kt @@ -0,0 +1,37 @@ +package com.example.accountbook.calendar + +import java.util.Calendar +import java.util.Date + + +object CalendarUtil { + fun getDayOfWeek(y: Int, m: Int, day: Int): Int { + val calendar: Calendar = Calendar.getInstance() + calendar.set(y, m - 1, day) + return calendar.get(Calendar.DAY_OF_WEEK) + } + + //获取一月最大天数 + fun getDayOfMaonth(y: Int, m: Int): Int { + val cal: Calendar = Calendar.getInstance() + cal.set(y, m - 1, 1) + return cal.getActualMaximum(Calendar.DATE) + } + + fun getMothOfMonth(y: Int, m: Int): Int { + val cal: Calendar = Calendar.getInstance() + cal.set(y, m - 1, 1) + val dateOfMonth: Int = cal.get(Calendar.MONTH) + return dateOfMonth + 1 + } + + fun getYMD(date: Date?): IntArray { + val cal: Calendar = Calendar.getInstance() + date?.let { cal.setTime(it) } + return intArrayOf( + cal.get(Calendar.YEAR), + cal.get(Calendar.MONTH) + 1, + cal.get(Calendar.DATE) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/calendar/CalendarView.kt b/app/src/main/java/com/example/accountbook/calendar/CalendarView.kt new file mode 100644 index 0000000..3fc58c7 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/calendar/CalendarView.kt @@ -0,0 +1,197 @@ +package com.example.accountbook.calendar + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import android.util.Log +import android.view.View +import android.view.ViewGroup +import com.example.accountbook.calendar.CalendarUtil.getYMD +import java.util.Date + + +class CalendarView : ViewGroup { + private var selectPosition = -1 + private var adapter: CalendarAdapter? = null + private var data: List? = null + private var onItemClickListener: OnItemClickListener? = null + private var row = 6 + private val column = 7 + private var itemWidth = 0 + var itemHeight = 0 + private set + private var isToday = false + + interface OnItemClickListener { + fun onItemClick(view: View?, position: Int, bean: CalendarBean?) + } + + constructor(context: Context?, row: Int) : super(context) { + this.row = row + } + + fun setOnItemClickListener(onItemClickListener: OnItemClickListener?) { + this.onItemClickListener = onItemClickListener + } + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { + setWillNotDraw(false) + } + + fun setAdapter(adapter: CalendarAdapter?) { + this.adapter = adapter + } + + fun setData(data: List?, isToday: Boolean) { + this.data = data + this.isToday = isToday + setItem() + requestLayout() + } + + private fun setItem() { + selectPosition = -1 + if (adapter == null) { + throw RuntimeException("adapter is null,please setadapter") + } + Log.i("data >>> ","data ${data}") + if (adapter?.hasChildView() == true) { + for (i in data!!.indices) { + val bean = data!![i] + val chidView: View? = adapter?.getView( + getChildAt(i).also { v -> Log.i("data >>> ", "view $v") }, + this, + bean + ).also { v -> Log.i("data >>> ", "chidView $v") } + if (chidView != null && chidView != getChildAt(i)) { + addViewInLayout(chidView, i, chidView.layoutParams, true) + } + if (isToday && selectPosition == -1) { + val date = getYMD(Date()) + if (bean.year == date[0] && bean.month == date[1] && bean.day == date[2]) { + selectPosition = i + } + } else { + if (selectPosition == -1 && bean.day == 1) { + selectPosition = i + } + } + chidView?.isSelected = selectPosition == i + setItemClick(chidView, i, bean) + } + } + } + + val select: Array + get() = arrayOf( + getChildAt(selectPosition), selectPosition, + data!![selectPosition] + ) + + fun setItemClick(view: View?, potsion: Int, bean: CalendarBean?) { + view?.setOnClickListener { + if (selectPosition != -1) { + getChildAt(selectPosition).isSelected = false + getChildAt(potsion).isSelected = true + } + selectPosition = potsion + if (onItemClickListener != null) { + onItemClickListener!!.onItemClick(view, potsion, bean) + } + } + } + + fun getSelectPosition(): IntArray { + val rect = Rect() + try { + getChildAt(selectPosition).getHitRect(rect) + } catch (e: Exception) { + e.printStackTrace() + } + return intArrayOf(rect.left, rect.top, rect.right, rect.top) + } + + + var lastwidthMeasureSpec = 0 + var lastheightMeasureSpec = 0 + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + Log.e(this.javaClass::class.java.simpleName , "this count check ${this}") + if (widthMeasureSpec != lastwidthMeasureSpec || heightMeasureSpec != lastheightMeasureSpec) { + val parentWidth = + MeasureSpec.getSize( + MeasureSpec.makeMeasureSpec( + widthMeasureSpec, + MeasureSpec.EXACTLY + ) + ) + val parentH = + MeasureSpec.getSize( + MeasureSpec.makeMeasureSpec( + heightMeasureSpec, + MeasureSpec.EXACTLY + ) + ) + itemWidth = parentWidth / column + itemHeight = parentH / row +// itemWidth + val view = getChildAt(0) ?: return + val params = view.layoutParams + if (params != null && params.height > 0) { + itemHeight = params.height + } + setMeasuredDimension(parentWidth, itemHeight * row) + if (childCount.equals(row*column)) + for (i in 0 until childCount) { + val childView = getChildAt(i) + childView.measure( + MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY) + ) + } + } + Log.i( + TAG, + "onMeasure() called with: itemHeight = [$itemHeight], itemWidth = [$itemWidth]" + ) + lastwidthMeasureSpec = widthMeasureSpec + lastheightMeasureSpec = heightMeasureSpec + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + if (changed && childCount.equals(row*column)) + for (i in 0 until childCount) { + layoutChild(getChildAt(i), i, l, t, r, b) + } + } + + override fun addViewInLayout( + child: View?, + index: Int, + params: LayoutParams?, + preventRequestLayout: Boolean + ): Boolean { + Log.d("CalendarView, time","time") + return super.addViewInLayout(child, index, params, preventRequestLayout) + } + + private fun layoutChild(view: View, position: Int, l: Int, t: Int, r: Int, b: Int) { + var l = l + var t = t + var r = r + var b = b + val cc = position % column + val cr = position / column + val itemWidth = view.measuredWidth + val itemHeight = view.measuredHeight + l = cc * itemWidth + t = cr * itemHeight + r = l + itemWidth + b = t + itemHeight + view.layout(l, t, r, b) + } + + companion object { + private const val TAG = "CalendarView" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/fragment/FragmentCalendar.kt b/app/src/main/java/com/example/accountbook/fragment/FragmentCalendar.kt new file mode 100644 index 0000000..50ead5e --- /dev/null +++ b/app/src/main/java/com/example/accountbook/fragment/FragmentCalendar.kt @@ -0,0 +1,113 @@ +package com.example.accountbook.fragment + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.accountbook.activity.ActivityWriter +import com.example.accountbook.calendar.CalendarAdapter +import com.example.accountbook.calendar.CalendarBean +import com.example.accountbook.databinding.FragmentCalendarBinding +import com.example.accountbook.databinding.ItemCalendarBinding + +class FragmentCalendar: Fragment() { + private lateinit var bind: FragmentCalendarBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + bind = FragmentCalendarBinding.inflate(inflater) + return bind.root + } + + override fun onStart() { + super.onStart() + // init view + if (bind.calendarDateView.adapter == null) { + initView() + } + // init list +// initList() + } + + private fun initView() { + Log.i("FragmentCalendar","initView") + + bind.calendarDateView.setAdapter(object: CalendarAdapter { + override fun getView( + convertView: View?, + parentView: ViewGroup?, + bean: CalendarBean? + ): View { + Log.i("FragmentCalendar","getView") + + var bindItem : ItemCalendarBinding? = null + if (convertView == null) { + bindItem = ItemCalendarBinding.inflate(LayoutInflater.from(parentView?.context)) + } else { + bindItem = ItemCalendarBinding.bind(convertView) + } + bindItem.day.apply { + this.text = bean?.day.toString() + if (bean?.monthFlag != 0) { + this.setTextColor(Color.WHITE) + } else { + this.setTextColor(Color.BLACK) + } + } + + bindItem.root.setOnLongClickListener{ it -> + startActivity(Intent(context, ActivityWriter::class.java)) + return@setOnLongClickListener true + } + return convertView ?: bindItem.root + } + + override fun hasChildView() = bind.calendarDateView.views.size > 0 + }) + } + + override fun onAttach(context: Context) { + super.onAttach(context) + } + + override fun onResume() { + super.onResume() + } + +// private fun initList() { +// bind.list.setAdapter(object : BaseAdapter() { +// override fun getCount(): Int { +// Log.i("bindList" , "getCount ") +// return 100 +// } +// +// override fun getItem(position: Int) = null +// +// override fun getItemId(position: Int): Long = 0 +// +// override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { +// Log.i("bindList" , "getView ${position}") +// var mConvertView: View? = convertView +// if (mConvertView == null) { +// mConvertView = LayoutInflater.from(this@ActivityCalendar).inflate(android.R.layout.simple_list_item_1, null) +// } +// +// (mConvertView as TextView).apply { +// this.text = "item" + position +// this.setBackgroundColor(Color.RED) +// } +// +// return mConvertView +// } +// +// }) +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/accountbook/fragment/FragmentTable.kt b/app/src/main/java/com/example/accountbook/fragment/FragmentTable.kt new file mode 100644 index 0000000..0772f82 --- /dev/null +++ b/app/src/main/java/com/example/accountbook/fragment/FragmentTable.kt @@ -0,0 +1,33 @@ +package com.example.accountbook.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.Fragment +import com.example.accountbook.R +import com.example.accountbook.adapter.AdapterTable +import com.example.accountbook.databinding.FragmentTableBinding + +class FragmentTable: Fragment() { + private lateinit var bind: FragmentTableBinding + private var li: Array = arrayOf("0", "1") + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // view + bind = FragmentTableBinding.inflate(inflater) + // data + setData() + bind.listTable.adapter = AdapterTable(inflater.context, li) + return bind.root + } + + private fun setData() { + + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_calendar.xml b/app/src/main/res/layout/activity_calendar.xml new file mode 100644 index 0000000..46c5f4c --- /dev/null +++ b/app/src/main/res/layout/activity_calendar.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + +