002.逸話百科事典/ QiuShiBaiKe [android]



002 Anecdote Encyclopedia Qiushibaike



2番目のアプリケーションは 恥ずかしさ百科事典

ソースコード: https://github.com/iOSDevLog/1day1app



910914-9eb38c77caad887e.jpeg002.QiuShiBaiKe

需要を分析します。

  1. から http://qiushibaike.com 取るjsonデータ
  2. jsonへのデータ変換kotlin データクラス
  3. データを表示する

開発

  • 作成する Android Studio 事業

  • AndroidManifest.xml トップウィンドウの権限を追加する



implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.34' implementation 'io.reactivex.rxjava2:rxjava:2.1.0' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.android.support:recyclerview-v7:27.0.2' implementation 'com.android.support:cardview-v7:27.0.2'
  • build.gradle(Module:app) 依存ライブラリを追加する
interface IQBRequest { @GET('article/list/text') fun fetchQBEntity(@Query('page') page: Int): Observable }
  • 作成する QBEntity

プラグインの使用 JsonToKotlinClass

  • 作成する IQBRequest
class QBRequestUnitTest { private val BASE_URL = 'http://m2.qiushibaike.com/' @Test fun request_isSuccess() { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() val qbRequest = retrofit.create(IQBRequest::class.java) val page = 1 qbRequest.fetchQBEntity(page) .subscribe({ result -> println(result) assert(result.items.count() > 0) assert(result.count > 0) }, { error -> println('onError') error.printStackTrace() assert(false) }, { println('onComplete') assert(true) }, { println('start') }) } }
  • テスト IQBRequest
class PullToRefreshAdapter : BaseQuickAdapter(R.layout.qb_cell, ArrayList()) { override fun convert(helper: BaseViewHolder, item: Item) { helper.setImageResource(R.id.head_imageview, R.drawable.iosdevlog) helper.setText(R.id.content_textview, item.content) } }
  • 表示セルを作成する
RecyclerView
  • 作成する アダプターの更新
// LifeCycle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Thread.setDefaultUncaughtExceptionHandler(this) mRecyclerView = findViewById(R.id.recycleview) mSwipeRefreshLayout = findViewById(R.id.swipeLayout) mSwipeRefreshLayout.setOnRefreshListener(this) mSwipeRefreshLayout.setColorSchemeColors(Color.YELLOW) mRecyclerView.layoutManager = LinearLayoutManager(this) initAdapter() } // Helper private fun initAdapter() { pullToRefreshAdapter = PullToRefreshAdapter() pullToRefreshAdapter.setOnLoadMoreListener(this, mRecyclerView) pullToRefreshAdapter.openLoadAnimation(BaseQuickAdapter.SLIDEIN_BOTTOM) mRecyclerView.adapter = pullToRefreshAdapter mRecyclerView.addOnItemTouchListener(object : OnItemClickListener() { override fun onSimpleItemClick(adapter: BaseQuickAdapter, view: View, position: Int) { Toast.makeText(root@xxxxx, Integer.toString(position), Toast.LENGTH_LONG).show() } }) }
  • activity_main BaseQuickAdapter.RequestLoadMoreListenerに追加
// Refresh override fun onLoadMoreRequested() { mSwipeRefreshLayout.isEnabled = false fetch(currentPage).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ result -> Log.d(TAG, result.toString()) pullToRefreshAdapter.addData(result.items) }, { error -> error.printStackTrace() mSwipeRefreshLayout.isEnabled = true mSwipeRefreshLayout.isRefreshing = false Toast.makeText(root@xxxxx, R.string.network_error, Toast.LENGTH_LONG).show() pullToRefreshAdapter.setEnableLoadMore(true) pullToRefreshAdapter.loadMoreFail() }, { Log.d(TAG, 'onComplete') currentPage += 1 mSwipeRefreshLayout.isEnabled = true mSwipeRefreshLayout.isRefreshing = false pullToRefreshAdapter.setEnableLoadMore(true) pullToRefreshAdapter.loadMoreComplete() }, { Log.d(TAG, 'onStart') }) } override fun onRefresh() { pullToRefreshAdapter.setEnableLoadMore(false) currentPage = 1 fetch(currentPage).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ result -> Log.d(TAG, result.toString()) pullToRefreshAdapter.setNewData(result.items) }, { error -> Log.d(TAG, 'onError') error.printStackTrace() mSwipeRefreshLayout.isRefreshing = false pullToRefreshAdapter.setEnableLoadMore(true) }, { Log.d(TAG, 'onComplete') mSwipeRefreshLayout.isRefreshing = false pullToRefreshAdapter.setEnableLoadMore(true) currentPage += 1 }, { Log.d(TAG, 'onStart') }) } private fun fetch(page: Int): Observable { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() val qbRequest = retrofit.create(IQBRequest::class.java) return qbRequest.fetchQBEntity(page) }
  • MainActivity 初期化
Thread.UncaughtExceptionHandler
  • MainActivity 達成 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Thread.setDefaultUncaughtExceptionHandler(this) ... } // UncaughtExceptionHandler override fun uncaughtException(t: Thread?, e: Throwable?) = e!!.printStackTrace() 、1SwipeRefreshLayout.OnRefreshListener`
override fun onResume() { super.onResume() Handler().postDelayed({ mSwipeRefreshLayout.isRefreshing = true onRefresh() }, 10) }
  • (オプション) MainActivity 実現 override fun convert(helper: BaseViewHolder, item: Item) { if (item.user != null) { val imageView = helper.getView(R.id.head_imageview) Picasso.with(mContext) .load('http:' + item.user.medium) .resize(40, 40) .centerCrop() .transform(CircleTransform()) .into(imageView) helper.setText(R.id.username_textview, item.user.login) helper.setText(R.id.username_detail, item.user.age.toString() + ' ' + item.user.astrology + ' Like: ' + item.votes.up + ' Step: ' + item.votes.down ) val gender = when (item.user.gender) { 'F' -> R.drawable.female 'M' -> R.drawable.male else -> R.drawable.male } helper.setImageResource(R.id.gender_imageview, gender) } val published_at = java.lang.Long.valueOf(item.published_at) val simpleDateFormat = SimpleDateFormat('yyyy-MM-dd HH:mm:ss') val published_string = simpleDateFormat.format( Date(published_at * 1000L)) helper.setText(R.id.datetime_textview, published_string) helper.setText(R.id.content_textview, item.content) } inner class CircleTransform : Transformation { override fun transform(source: Bitmap): Bitmap { val size = Math.min(source.width, source.height) val x = (source.width - size) / 2 val y = (source.height - size) / 2 val squaredBitmap = Bitmap.createBitmap(source, x, y, size, size) if (squaredBitmap != source) { source.recycle() } val config = if (source.config != null) source.config else Bitmap.Config.ARGB_8888 val bitmap = Bitmap.createBitmap(size, size, config) val canvas = Canvas(bitmap) val paint = Paint() val shader = BitmapShader(squaredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) paint.setShader(shader) paint.setAntiAlias(true) val r = size / 2f canvas.drawCircle(r, r, r, paint) squaredBitmap.recycle() return bitmap } override fun key(): String { return 'circle' } }
  • インターフェースは最新のデータをリアルタイムで表示できます。
|_+_|
  • ユーザー情報を読み込む

参照: https://gist.github.com/julianshen/5829333

|_+_|