لایه رابط کاربری (Views)

مفاهیم و پیاده‌سازی Jetpack Compose

نقش رابط کاربری نمایش داده‌های برنامه روی صفحه و همچنین ایفای نقش به عنوان نقطه اصلی تعامل کاربر است. هر زمان که داده‌ها تغییر کنند، چه به دلیل تعامل کاربر (مانند فشار دادن یک دکمه) یا ورودی خارجی (مانند پاسخ شبکه)، رابط کاربری باید به‌روزرسانی شود تا آن تغییرات را منعکس کند. در واقع، رابط کاربری یک نمایش بصری از وضعیت برنامه است که از لایه داده بازیابی می‌شود.

با این حال، داده‌های برنامه‌ای که از لایه داده دریافت می‌کنید، معمولاً در قالبی متفاوت از اطلاعاتی است که باید نمایش دهید. به عنوان مثال، ممکن است فقط به بخشی از داده‌ها برای رابط کاربری نیاز داشته باشید، یا ممکن است لازم باشد دو منبع داده مختلف را برای ارائه اطلاعات مرتبط با کاربر ادغام کنید. صرف نظر از منطقی که اعمال می‌کنید، باید تمام اطلاعات مورد نیاز برای رندر کامل را به رابط کاربری منتقل کنید. لایه رابط کاربری، خط لوله‌ای است که تغییرات داده‌های برنامه را به شکلی تبدیل می‌کند که رابط کاربری بتواند آن را ارائه دهد و سپس نمایش دهد.

نمایش وضعیت رابط کاربری

بعد از اینکه وضعیت رابط کاربری خود را تعریف کردید و نحوه مدیریت تولید آن وضعیت را تعیین کردید، مرحله بعدی ارائه وضعیت تولید شده به رابط کاربری است. از آنجا که شما از UDF برای مدیریت تولید وضعیت استفاده می‌کنید، می‌توانید وضعیت تولید شده را به عنوان یک جریان در نظر بگیرید - به عبارت دیگر، چندین نسخه از وضعیت در طول زمان تولید می‌شود. در نتیجه، باید وضعیت رابط کاربری را در یک نگهدارنده داده قابل مشاهده مانند LiveData یا StateFlow نمایش دهید. دلیل این کار این است که رابط کاربری بتواند بدون نیاز به دریافت دستی داده‌ها مستقیماً از ViewModel، به هرگونه تغییر ایجاد شده در وضعیت واکنش نشان دهد. این نوع‌ها همچنین از این مزیت برخوردارند که همیشه آخرین نسخه از وضعیت رابط کاربری را در حافظه پنهان دارند که برای بازیابی سریع وضعیت پس از تغییرات پیکربندی مفید است.

class NewsViewModel(...) : ViewModel() {

    val uiState: StateFlow<NewsUiState> = 
}

یک روش رایج برای ایجاد یک جریان از UiState ، نمایش یک جریان تغییرپذیر پشتیبان به عنوان یک جریان تغییرناپذیر از ViewModel است - برای مثال، نمایش یک MutableStateFlow<UiState> به عنوان یک StateFlow<UiState> .

class NewsViewModel(...) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    ...

}

سپس ViewModel می‌تواند متدهایی را که به صورت داخلی وضعیت را تغییر می‌دهند، نمایش دهد و به‌روزرسانی‌هایی را برای استفاده توسط رابط کاربری منتشر کند. برای مثال، موردی را در نظر بگیرید که در آن یک عمل ناهمزمان باید انجام شود؛ یک کوروتین می‌تواند با استفاده از viewModelScope راه‌اندازی شود و وضعیت قابل تغییر می‌تواند پس از اتمام به‌روزرسانی شود.

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                _uiState.update {
                    it.copy(newsItems = newsItems)
                }
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                _uiState.update {
                    val messages = getMessagesFromThrowable(ioe)
                    it.copy(userMessages = messages)
                }
            }
        }
    }
}

مصرف وضعیت رابط کاربری

هنگام استفاده از نگهدارنده‌های داده قابل مشاهده در رابط کاربری، مطمئن شوید که چرخه حیات رابط کاربری را در نظر می‌گیرید. این مهم است زیرا رابط کاربری نباید وضعیت رابط کاربری را زمانی که نما به کاربر نمایش داده نمی‌شود، مشاهده کند. برای کسب اطلاعات بیشتر در مورد این موضوع، به این پست وبلاگ مراجعه کنید. هنگام استفاده از LiveData ، LifecycleOwner به طور ضمنی به نگرانی‌های چرخه حیات رسیدگی می‌کند. هنگام استفاده از جریان‌ها، بهتر است این کار را با محدوده کوروتین مناسب و API repeatOnLifecycle مدیریت کنید:

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

نمایش عملیات در حال انجام

یک راه ساده برای نمایش حالت‌های بارگذاری در یک کلاس UiState ، استفاده از یک فیلد بولی است:

data class NewsUiState(
    val isFetchingArticles: Boolean = false,
    ...
)

مقدار این پرچم، وجود یا عدم وجود نوار پیشرفت در رابط کاربری را نشان می‌دهد.

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Bind the visibility of the progressBar to the state
                // of isFetchingArticles.
                viewModel.uiState
                    .map { it.isFetchingArticles }
                    .distinctUntilChanged()
                    .collect { progressBar.isVisible = it }
            }
        }
    }
}

انیمیشن‌ها

برای اینکه انتقال‌های ناوبری سطح بالا روان و روان باشند، ممکن است بخواهید قبل از شروع انیمیشن، منتظر بارگذاری داده‌ها در صفحه دوم باشید. چارچوب نمای اندروید، قلاب‌هایی را برای تأخیر انتقال بین مقاصد قطعه کد با APIهای postponeEnterTransition() و startPostponedEnterTransition() ارائه می‌دهد. این APIها راهی را برای اطمینان از آماده بودن عناصر رابط کاربری در صفحه دوم (معمولاً تصویری که از شبکه گرفته شده است) برای نمایش قبل از اینکه رابط کاربری انتقال به آن صفحه را متحرک کند، فراهم می‌کنند.

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}