하위 뷰에 대한 상대 Rect 정보 가져오기 및 덮어쓰기에서 잘라내기
18512 단어 Android
입문
Android Advent Calendar 2019의 12일째.
오늘 젓가락을 쉴 때 가장 최근의 기술을 쓰는 것이 아니라View 조작에 관한 일을 쓴다.
제목은'자손의 보기에 대한 상대적 Rect 정보를 얻고 중첩에서 잘라내기'입니다. 이번에는 Activity에서 중첩된 자체 샘플링 코드를 잘라낸 것을 바탕으로 포함된 Fragent가 가지고 있는 보기의 특정 부모 보기 그룹에서 상대적 Rect(직사각형 좌표) 정보를 얻고 조작하는 예를 설명합니다.
나는 매우 추상적인 설명이 이해하기 어렵다고 생각해서 그림으로 설명해 주었다.
액티비티가 MainFragment와 전체 액티비티를 덮어쓰는 MaskView(반투명 덮어쓰기)를 레이아웃으로 한다고 가정합니다.
※ MaskView는 독자적으로 정의된 사용자 정의 보기로 덮어쓰는 색과 특정한 Rect 범위를 투명하게 하는 방법이 있습니다.잠시 후 터치하세요.
그리고 Fragment가 가지고 있는 특정View(위 이미지에서 말한 Pic1)의, Activity로부터의 상대적인 Rect 정보를 가져와 이 좌표 범위의 MaskView를 동적 투명하게 하여'잘라내기'처럼 보일 수 있습니다.
gif 이미지는 다음과 같습니다.여기서 FAB를 눌렀을 때 첫 번째 이미지 범위의 덮어쓰기를 잘라냅니다.
또한 샘플 응용 프로그램은 아래부터 시작하십시오.
포인트는 OffsetDescendantRectToMyCoords입니다.
여기서 중요한 점은 ViewGroup의 방법offsetDescendantRectToMyCoords입니다.
Offset a rectangle that is in a descendant's coordinate space into our coordinate space.
자손 보기에 여분 등을 지정하거나 중첩된 차원wrap_content
을 사용하더라도 이에 따라 부모 보기 그룹 좌표 공간의 좌표를 얻을 수 있다.
이 경우 다음과 같이 Activity 레이아웃에는 container
및 하위 main_fragment
가 있습니다.이 main_fragment
를 포함하는View의container에서 상대적인 Rect 정보를 얻으면 전체 container를 덮어쓰는 mask
에서 대상 범위를 잘라낼 수 있습니다.
레이아웃 예<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_fragment"
android:name="com.example.nichiyoshi.maskclipsample.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.example.nichiyoshi.maskclipsample.MaskView
android:id="@+id/mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_scissors"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
사전 준비: 사용자 정의 MaskView
다음 MaskView는 반투명 검은색(#800000)으로 View 범위를 채우고 clipWithRect(rect: Rect)
메서드를 호출할 때 매개변수가 지정한 Rect 범위를 투명하게 다시 그립니다.
MaskViewclass MaskView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint().apply {
color = Color.parseColor("#80000000")
}
private val clearPaint = Paint().apply {
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private var clipRect: Rect? = null
fun clipWithRect(rect: Rect) {
clipRect = rect
postInvalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
setLayerType(LAYER_TYPE_HARDWARE, null)
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), backgroundPaint)
clipRect?.let { rect ->
canvas.drawRect(rect, clearPaint)
}
}
}
그리고 참고할 수 있도록 허락해 주십시오이 문장.
총체적
Activity와 MainFragment에서 ViewModel을 공유하고 Activity FAB를 누르면 ViewModel의 requestViewToClip
함수를 호출합니다.이를 감시하는 MainFragment는ViewModel의 setViewToClip
함수에서 재단할 View (pic1)를 지정하고, 이를 감시하는 Activity는 아까의 offsetDescendantRectToMyCoords
함수에서container에서 상대적인 Rect 범위를 가져오고, MaskView에서 이 범위를 재단합니다.
MainActivityclass MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fab.setOnClickListener {
viewModel.requestViewToClip()
}
viewModel.viewToClip.observe(this) { targetView ->
Rect().apply {
targetView.getDrawingRect(this)
container.offsetDescendantRectToMyCoords(targetView, this)
mask.clipWithRect(this)
}
}
}
}
※ observe(this){}
에 해당하는 곳에서 사용KTX.
MainViewModelclass MainViewModel: ViewModel() {
private val _viewToClip = MutableLiveData<View>()
val viewToClip: LiveData<View> = _viewToClip
fun setViewToCLip(view: View) {
_viewToClip.postValue(view)
}
private val _requestViewToClip = MutableLiveData<Unit>()
val requestViewToClip: LiveData<Unit> = _requestViewToClip
fun requestViewToClip() {
_requestViewToClip.postValue(Unit)
}
}
MainFragmentclass MainFragment: Fragment(R.layout.fragment_main) {
private val viewModel by activityViewModels<MainViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
image1.scaleType = ImageView.ScaleType.FIT_CENTER
image2.scaleType = ImageView.ScaleType.FIT_CENTER
// loads images from free image provider "pakutaso"
image1.load("https://www.pakutaso.com/shared/img/thumb/cat9302341_TP_V.jpg")
image2.load("https://www.pakutaso.com/shared/img/thumb/cat9302331_TP_V.jpg")
viewModel.requestViewToClip.observe(viewLifecycleOwner) {
viewModel.setViewToCLip(image1)
}
}
}
※ by activityViewModels 뷰모델의 속성 삭제 입니다.
※ class MainFragment: Fragment(R.layout.fragment_main)
에 해당하는 곳에서 사용프레임 레이아웃 Id 구조 함수.
끝내다
다소 이해하기 어려운 설명이지만 여기서 끝난다.
소스 코트는 GitHub 거예요.
https://github.com/nichiyoshi/clip_mask_sample
Reference
이 문제에 관하여(하위 뷰에 대한 상대 Rect 정보 가져오기 및 덮어쓰기에서 잘라내기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/nichiyoshi/items/97ddc203291224c9cbc5
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
여기서 중요한 점은 ViewGroup의 방법offsetDescendantRectToMyCoords입니다.
Offset a rectangle that is in a descendant's coordinate space into our coordinate space.
자손 보기에 여분 등을 지정하거나 중첩된 차원
wrap_content
을 사용하더라도 이에 따라 부모 보기 그룹 좌표 공간의 좌표를 얻을 수 있다.이 경우 다음과 같이 Activity 레이아웃에는
container
및 하위 main_fragment
가 있습니다.이 main_fragment
를 포함하는View의container에서 상대적인 Rect 정보를 얻으면 전체 container를 덮어쓰는 mask
에서 대상 범위를 잘라낼 수 있습니다.레이아웃 예
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_fragment"
android:name="com.example.nichiyoshi.maskclipsample.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.example.nichiyoshi.maskclipsample.MaskView
android:id="@+id/mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_scissors"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
사전 준비: 사용자 정의 MaskView
다음 MaskView는 반투명 검은색(#800000)으로 View 범위를 채우고 clipWithRect(rect: Rect)
메서드를 호출할 때 매개변수가 지정한 Rect 범위를 투명하게 다시 그립니다.
MaskViewclass MaskView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint().apply {
color = Color.parseColor("#80000000")
}
private val clearPaint = Paint().apply {
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private var clipRect: Rect? = null
fun clipWithRect(rect: Rect) {
clipRect = rect
postInvalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
setLayerType(LAYER_TYPE_HARDWARE, null)
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), backgroundPaint)
clipRect?.let { rect ->
canvas.drawRect(rect, clearPaint)
}
}
}
그리고 참고할 수 있도록 허락해 주십시오이 문장.
총체적
Activity와 MainFragment에서 ViewModel을 공유하고 Activity FAB를 누르면 ViewModel의 requestViewToClip
함수를 호출합니다.이를 감시하는 MainFragment는ViewModel의 setViewToClip
함수에서 재단할 View (pic1)를 지정하고, 이를 감시하는 Activity는 아까의 offsetDescendantRectToMyCoords
함수에서container에서 상대적인 Rect 범위를 가져오고, MaskView에서 이 범위를 재단합니다.
MainActivityclass MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fab.setOnClickListener {
viewModel.requestViewToClip()
}
viewModel.viewToClip.observe(this) { targetView ->
Rect().apply {
targetView.getDrawingRect(this)
container.offsetDescendantRectToMyCoords(targetView, this)
mask.clipWithRect(this)
}
}
}
}
※ observe(this){}
에 해당하는 곳에서 사용KTX.
MainViewModelclass MainViewModel: ViewModel() {
private val _viewToClip = MutableLiveData<View>()
val viewToClip: LiveData<View> = _viewToClip
fun setViewToCLip(view: View) {
_viewToClip.postValue(view)
}
private val _requestViewToClip = MutableLiveData<Unit>()
val requestViewToClip: LiveData<Unit> = _requestViewToClip
fun requestViewToClip() {
_requestViewToClip.postValue(Unit)
}
}
MainFragmentclass MainFragment: Fragment(R.layout.fragment_main) {
private val viewModel by activityViewModels<MainViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
image1.scaleType = ImageView.ScaleType.FIT_CENTER
image2.scaleType = ImageView.ScaleType.FIT_CENTER
// loads images from free image provider "pakutaso"
image1.load("https://www.pakutaso.com/shared/img/thumb/cat9302341_TP_V.jpg")
image2.load("https://www.pakutaso.com/shared/img/thumb/cat9302331_TP_V.jpg")
viewModel.requestViewToClip.observe(viewLifecycleOwner) {
viewModel.setViewToCLip(image1)
}
}
}
※ by activityViewModels 뷰모델의 속성 삭제 입니다.
※ class MainFragment: Fragment(R.layout.fragment_main)
에 해당하는 곳에서 사용프레임 레이아웃 Id 구조 함수.
끝내다
다소 이해하기 어려운 설명이지만 여기서 끝난다.
소스 코트는 GitHub 거예요.
https://github.com/nichiyoshi/clip_mask_sample
Reference
이 문제에 관하여(하위 뷰에 대한 상대 Rect 정보 가져오기 및 덮어쓰기에서 잘라내기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/nichiyoshi/items/97ddc203291224c9cbc5
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
class MaskView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val backgroundPaint = Paint().apply {
color = Color.parseColor("#80000000")
}
private val clearPaint = Paint().apply {
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private var clipRect: Rect? = null
fun clipWithRect(rect: Rect) {
clipRect = rect
postInvalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
setLayerType(LAYER_TYPE_HARDWARE, null)
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), backgroundPaint)
clipRect?.let { rect ->
canvas.drawRect(rect, clearPaint)
}
}
}
Activity와 MainFragment에서 ViewModel을 공유하고 Activity FAB를 누르면 ViewModel의
requestViewToClip
함수를 호출합니다.이를 감시하는 MainFragment는ViewModel의 setViewToClip
함수에서 재단할 View (pic1)를 지정하고, 이를 감시하는 Activity는 아까의 offsetDescendantRectToMyCoords
함수에서container에서 상대적인 Rect 범위를 가져오고, MaskView에서 이 범위를 재단합니다.MainActivity
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fab.setOnClickListener {
viewModel.requestViewToClip()
}
viewModel.viewToClip.observe(this) { targetView ->
Rect().apply {
targetView.getDrawingRect(this)
container.offsetDescendantRectToMyCoords(targetView, this)
mask.clipWithRect(this)
}
}
}
}
※ observe(this){}
에 해당하는 곳에서 사용KTX.MainViewModel
class MainViewModel: ViewModel() {
private val _viewToClip = MutableLiveData<View>()
val viewToClip: LiveData<View> = _viewToClip
fun setViewToCLip(view: View) {
_viewToClip.postValue(view)
}
private val _requestViewToClip = MutableLiveData<Unit>()
val requestViewToClip: LiveData<Unit> = _requestViewToClip
fun requestViewToClip() {
_requestViewToClip.postValue(Unit)
}
}
MainFragmentclass MainFragment: Fragment(R.layout.fragment_main) {
private val viewModel by activityViewModels<MainViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
image1.scaleType = ImageView.ScaleType.FIT_CENTER
image2.scaleType = ImageView.ScaleType.FIT_CENTER
// loads images from free image provider "pakutaso"
image1.load("https://www.pakutaso.com/shared/img/thumb/cat9302341_TP_V.jpg")
image2.load("https://www.pakutaso.com/shared/img/thumb/cat9302331_TP_V.jpg")
viewModel.requestViewToClip.observe(viewLifecycleOwner) {
viewModel.setViewToCLip(image1)
}
}
}
※ by activityViewModels 뷰모델의 속성 삭제 입니다.※
class MainFragment: Fragment(R.layout.fragment_main)
에 해당하는 곳에서 사용프레임 레이아웃 Id 구조 함수.끝내다
다소 이해하기 어려운 설명이지만 여기서 끝난다.
소스 코트는 GitHub 거예요.
https://github.com/nichiyoshi/clip_mask_sample
Reference
이 문제에 관하여(하위 뷰에 대한 상대 Rect 정보 가져오기 및 덮어쓰기에서 잘라내기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/nichiyoshi/items/97ddc203291224c9cbc5
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(하위 뷰에 대한 상대 Rect 정보 가져오기 및 덮어쓰기에서 잘라내기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/nichiyoshi/items/97ddc203291224c9cbc5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)